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:11 UTC
[01/51] [abbrv] mina-sshd git commit: [SSHD-842] Split Putty key
files support code to separate artifact
Repository: mina-sshd
Updated Branches:
refs/heads/master 1e8254794 -> 3668d92e4
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java
new file mode 100644
index 0000000..366aead
--- /dev/null
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java
@@ -0,0 +1,65 @@
+/*
+ * 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.loader.putty;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+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.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DSSPuttyKeyDecoder extends AbstractPuttyKeyDecoder<DSAPublicKey, DSAPrivateKey> {
+ public static final DSSPuttyKeyDecoder INSTANCE = new DSSPuttyKeyDecoder();
+
+ public DSSPuttyKeyDecoder() {
+ super(DSAPublicKey.class, DSAPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_DSS));
+ }
+
+ @Override
+ public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
+ throws IOException, GeneralSecurityException {
+ pubReader.skip(); // skip version
+
+ BigInteger p = pubReader.readInt();
+ BigInteger q = pubReader.readInt();
+ BigInteger g = pubReader.readInt();
+ BigInteger y = pubReader.readInt();
+ BigInteger x = prvReader.readInt();
+ KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
+ PublicKey pubKey = kf.generatePublic(new DSAPublicKeySpec(y, p, q, g));
+ PrivateKey prvKey = kf.generatePrivate(new DSAPrivateKeySpec(x, p, q, g));
+ return Collections.singletonList(new KeyPair(pubKey, prvKey));
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java
new file mode 100644
index 0000000..a257ff8
--- /dev/null
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java
@@ -0,0 +1,98 @@
+/*
+ * 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.loader.putty;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ECDSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<ECPublicKey, ECPrivateKey> {
+ public static final ECDSAPuttyKeyDecoder INSTANCE = new ECDSAPuttyKeyDecoder();
+
+ public ECDSAPuttyKeyDecoder() {
+ super(ECPublicKey.class, ECPrivateKey.class, ECCurves.KEY_TYPES);
+ }
+
+ @Override
+ public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
+ throws IOException, GeneralSecurityException {
+ if (!SecurityUtils.isECCSupported()) {
+ throw new NoSuchAlgorithmException("ECC not supported for " + resourceKey);
+ }
+
+ String keyType = pubReader.readString();
+ ECCurves curve = ECCurves.fromKeyType(keyType);
+ if (curve == null) {
+ throw new InvalidKeySpecException("Not an EC curve name: " + keyType);
+ }
+
+ String encCurveName = pubReader.readString();
+ String keyCurveName = curve.getName();
+ if (!keyCurveName.equals(encCurveName)) {
+ throw new InvalidKeySpecException("Mismatched key curve name (" + keyCurveName + ") vs. encoded one (" + encCurveName + ")");
+ }
+
+ byte[] octets = pubReader.read();
+ ECPoint w;
+ try {
+ w = ECCurves.octetStringToEcPoint(octets);
+ if (w == null) {
+ throw new InvalidKeySpecException("No public ECPoint generated for curve=" + keyCurveName
+ + " from octets=" + BufferUtils.toHex(':', octets));
+ }
+ } catch (RuntimeException e) {
+ throw new InvalidKeySpecException("Failed (" + e.getClass().getSimpleName() + ")"
+ + " to generate public ECPoint for curve=" + keyCurveName
+ + " from octets=" + BufferUtils.toHex(':', octets)
+ + ": " + e.getMessage());
+ }
+
+ KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
+ ECParameterSpec paramSpec = curve.getParameters();
+ PublicKey pubKey = kf.generatePublic(new ECPublicKeySpec(w, paramSpec));
+
+ BigInteger s = prvReader.readInt();
+ PrivateKey prvKey = kf.generatePrivate(new ECPrivateKeySpec(s, paramSpec));
+ return Collections.singletonList(new KeyPair(pubKey, prvKey));
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java
new file mode 100644
index 0000000..f5980ab
--- /dev/null
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java
@@ -0,0 +1,68 @@
+/*
+ * 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.loader.putty;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderUtils;
+
+import net.i2p.crypto.eddsa.EdDSAPrivateKey;
+import net.i2p.crypto.eddsa.EdDSAPublicKey;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class EdDSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<EdDSAPublicKey, EdDSAPrivateKey> {
+ public static final EdDSAPuttyKeyDecoder INSTANCE = new EdDSAPuttyKeyDecoder();
+
+ public EdDSAPuttyKeyDecoder() {
+ super(EdDSAPublicKey.class, EdDSAPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_ED25519));
+ }
+
+ @Override
+ public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
+ throws IOException, GeneralSecurityException {
+ if (!SecurityUtils.isEDDSACurveSupported()) {
+ throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " provider not supported for " + resourceKey);
+ }
+
+ String keyType = pubReader.readString();
+ if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
+ throw new InvalidKeySpecException("Not an " + SecurityUtils.EDDSA + " key: " + keyType);
+ }
+
+ byte[] seed = pubReader.read();
+ PublicKey pubKey = EdDSASecurityProviderUtils.generateEDDSAPublicKey(seed);
+ seed = prvReader.read();
+ PrivateKey prvKey = EdDSASecurityProviderUtils.generateEDDSAPrivateKey(seed);
+ return Collections.singletonList(new KeyPair(pubKey, prvKey));
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java
new file mode 100644
index 0000000..06443d9
--- /dev/null
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java
@@ -0,0 +1,200 @@
+/*
+ * 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.loader.putty;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.config.keys.IdentityResourceLoader;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+import org.apache.sshd.common.digest.BuiltinDigests;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+//CHECKSTYLE:OFF
+/**
+ * Loads a {@link KeyPair} from PuTTY's ".ppk" file.
+ * <P>Note(s):</P>
+ * <UL>
+ * <P><LI>
+ * The file appears to be a text file but it doesn't have a fixed encoding like UTF-8.
+ * We use UTF-8 as the default encoding - since the important part is all ASCII,
+ * this shouldn't really hurt the interpretation of the key.
+ * </LI></P>
+ *
+ * <P><LI>
+ * Based on code from <A HREF="https://github.com/kohsuke/trilead-putty-extension">Kohsuke's Trilead Putty Extension</A>
+ * </LI></P>
+ *
+ * <P><LI>
+ * Encrypted keys requires AES-256-CBC support, which is available only if the
+ * <A HREF="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html">
+ * Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files</A> are installed
+ * </LI></P>
+ * </UL>
+ *
+ * <P>Sample PuTTY file format</P>
+ * <PRE>
+ * PuTTY-User-Key-File-2: ssh-rsa
+ * Encryption: none
+ * Comment: rsa-key-20080514
+ * Public-Lines: 4
+ * AAAAB3NzaC1yc2EAAAABJQAAAIEAiPVUpONjGeVrwgRPOqy3Ym6kF/f8bltnmjA2
+ * BMdAtaOpiD8A2ooqtLS5zWYuc0xkW0ogoKvORN+RF4JI+uNUlkxWxnzJM9JLpnvA
+ * HrMoVFaQ0cgDMIHtE1Ob1cGAhlNInPCRnGNJpBNcJ/OJye3yt7WqHP4SPCCLb6nL
+ * nmBUrLM=
+ * Private-Lines: 8
+ * AAAAgGtYgJzpktzyFjBIkSAmgeVdozVhgKmF6WsDMUID9HKwtU8cn83h6h7ug8qA
+ * hUWcvVxO201/vViTjWVz9ALph3uMnpJiuQaaNYIGztGJBRsBwmQW9738pUXcsUXZ
+ * 79KJP01oHn6Wkrgk26DIOsz04QOBI6C8RumBO4+F1WdfueM9AAAAQQDmA4hcK8Bx
+ * nVtEpcF310mKD3nsbJqARdw5NV9kCxPnEsmy7Sy1L4Ob/nTIrynbc3MA9HQVJkUz
+ * 7V0va5Pjm/T7AAAAQQCYbnG0UEekwk0LG1Hkxh1OrKMxCw2KWMN8ac3L0LVBg/Tk
+ * 8EnB2oT45GGeJaw7KzdoOMFZz0iXLsVLNUjNn2mpAAAAQQCN6SEfWqiNzyc/w5n/
+ * lFVDHExfVUJp0wXv+kzZzylnw4fs00lC3k4PZDSsb+jYCMesnfJjhDgkUA0XPyo8
+ * Emdk
+ * Private-MAC: 50c45751d18d74c00fca395deb7b7695e3ed6f77
+ * </PRE>
+ * @param <PUB> Generic public key type
+ * @param <PRV> Generic private key type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+//CHECKSTYLE:ON
+public interface PuttyKeyPairResourceParser<PUB extends PublicKey, PRV extends PrivateKey>
+ extends IdentityResourceLoader<PUB, PRV>, KeyPairResourceParser {
+ String KEY_FILE_HEADER_PREFIX = "PuTTY-User-Key-File";
+ String PUBLIC_LINES_HEADER = "Public-Lines";
+ String PRIVATE_LINES_HEADER = "Private-Lines";
+ String PPK_FILE_SUFFIX = ".ppk";
+
+ List<String> KNOWN_HEADERS =
+ Collections.unmodifiableList(
+ Arrays.asList(
+ KEY_FILE_HEADER_PREFIX,
+ PUBLIC_LINES_HEADER,
+ PRIVATE_LINES_HEADER));
+
+ /**
+ * Value (case insensitive) used to denote that private key is not encrypted
+ */
+ String NO_PRIVATE_KEY_ENCRYPTION_VALUE = "none";
+
+ @Override
+ default boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+ throws IOException, GeneralSecurityException {
+ if (GenericUtils.isEmpty(lines)) {
+ return false;
+ }
+
+ for (String l : lines) {
+ l = GenericUtils.trimToEmpty(l);
+ for (String hdr : KNOWN_HEADERS) {
+ if (l.startsWith(hdr)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ static byte[] decodePrivateKeyBytes(byte[] prvBytes, String algName, int numBits, String algMode, String password)
+ throws GeneralSecurityException {
+ Objects.requireNonNull(prvBytes, "No encrypted key bytes");
+ ValidateUtils.checkNotNullAndNotEmpty(algName, "No encryption algorithm", GenericUtils.EMPTY_OBJECT_ARRAY);
+ ValidateUtils.checkTrue(numBits > 0, "Invalid encryption key size: %d", numBits);
+ ValidateUtils.checkNotNullAndNotEmpty(algMode, "No encryption mode", GenericUtils.EMPTY_OBJECT_ARRAY);
+ ValidateUtils.checkNotNullAndNotEmpty(password, "No encryption password", GenericUtils.EMPTY_OBJECT_ARRAY);
+
+ if (!"AES".equalsIgnoreCase(algName)) {
+ throw new NoSuchAlgorithmException("decodePrivateKeyBytes(" + algName + "-" + numBits + "-" + algMode + ") N/A");
+ }
+
+ return decodePrivateKeyBytes(prvBytes, algName, algMode, numBits, new byte[16], toEncryptionKey(password));
+ }
+
+ static byte[] decodePrivateKeyBytes(
+ byte[] encBytes, String cipherName, String cipherMode, int numBits, byte[] initVector, byte[] keyValue)
+ throws GeneralSecurityException {
+ String xform = cipherName + "/" + cipherMode + "/NoPadding";
+ int maxAllowedBits = Cipher.getMaxAllowedKeyLength(xform);
+ // see http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
+ if (numBits > maxAllowedBits) {
+ throw new InvalidKeySpecException("decodePrivateKeyBytes(" + xform + ")"
+ + " required key length (" + numBits + ") exceeds max. available: " + maxAllowedBits);
+ }
+
+ SecretKeySpec skeySpec = new SecretKeySpec(keyValue, cipherName);
+ IvParameterSpec ivspec = new IvParameterSpec(initVector);
+ Cipher cipher = SecurityUtils.getCipher(xform);
+ cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivspec);
+
+ return cipher.doFinal(encBytes);
+ }
+
+ /**
+ * Converts a pass-phrase into a key, by following the convention that PuTTY uses.
+ * Used to decrypt the private key when it's encrypted.
+ * @param passphrase the Password to be used as seed for the key - ignored
+ * if {@code null}/empty
+ * @return The encryption key bytes - {@code null} if no pass-phrase
+ * @throws GeneralSecurityException If cannot retrieve SHA-1 digest
+ * @see <A HREF="http://security.stackexchange.com/questions/71341/how-does-putty-derive-the-encryption-key-in-its-ppk-format">
+ * How does Putty derive the encryption key in its .ppk format ?</A>
+ */
+ static byte[] toEncryptionKey(String passphrase) throws GeneralSecurityException {
+ if (GenericUtils.isEmpty(passphrase)) {
+ return null;
+ }
+
+ MessageDigest hash = SecurityUtils.getMessageDigest(BuiltinDigests.sha1.getAlgorithm());
+ byte[] stateValue = {0, 0, 0, 0};
+ byte[] passBytes = passphrase.getBytes(StandardCharsets.UTF_8);
+ byte[] keyValue = new byte[32];
+ for (int i = 0, remLen = keyValue.length; i < 2; i++) {
+ hash.reset(); // just making sure
+
+ stateValue[3] = (byte) i;
+ hash.update(stateValue);
+ hash.update(passBytes);
+
+ byte[] digest = hash.digest();
+ System.arraycopy(digest, 0, keyValue, i * 20, Math.min(20, remLen));
+ remLen -= 20;
+ }
+
+ return keyValue;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java
new file mode 100644
index 0000000..4fb63d1
--- /dev/null
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java
@@ -0,0 +1,75 @@
+/*
+ * 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.loader.putty;
+
+import java.io.Closeable;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Helper class for {@code Putty} key files decoders
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class PuttyKeyReader implements Closeable {
+ private final DataInputStream di;
+
+ public PuttyKeyReader(InputStream s) {
+ di = new DataInputStream(s);
+ }
+
+ public void skip() throws IOException {
+ int skipSize = di.readInt();
+ int effectiveSkip = di.skipBytes(skipSize);
+ if (skipSize != effectiveSkip) {
+ throw new StreamCorruptedException("Mismatched skip size: expected" + skipSize + ", actual=" + effectiveSkip);
+ }
+ }
+
+ public String readString() throws IOException {
+ return readString(StandardCharsets.UTF_8);
+ }
+
+ public String readString(Charset cs) throws IOException {
+ byte[] data = read();
+ return new String(data, cs);
+ }
+
+ public BigInteger readInt() throws IOException {
+ byte[] bytes = read();
+ return new BigInteger(bytes);
+ }
+
+ public byte[] read() throws IOException {
+ int len = di.readInt();
+ byte[] r = new byte[len];
+ di.readFully(r);
+ return r;
+ }
+
+ @Override
+ public void close() throws IOException {
+ di.close();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java
new file mode 100644
index 0000000..e750ace
--- /dev/null
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java
@@ -0,0 +1,67 @@
+/*
+ * 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.loader.putty;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class PuttyKeyUtils {
+ public static final List<PuttyKeyPairResourceParser<?, ?>> DEFAULT_PARSERS;
+
+ public static final NavigableMap<String, PuttyKeyPairResourceParser<?, ?>> BY_KEY_TYPE;
+
+ public static final KeyPairResourceParser DEFAULT_INSTANCE;
+
+ static {
+ List<PuttyKeyPairResourceParser<?, ?>> parsers = new ArrayList<>();
+ parsers.add(RSAPuttyKeyDecoder.INSTANCE);
+ parsers.add(DSSPuttyKeyDecoder.INSTANCE);
+ if (SecurityUtils.isECCSupported()) {
+ parsers.add(ECDSAPuttyKeyDecoder.INSTANCE);
+ }
+ if (SecurityUtils.isEDDSACurveSupported()) {
+ parsers.add(EdDSAPuttyKeyDecoder.INSTANCE);
+ }
+ NavigableMap<String, PuttyKeyPairResourceParser<?, ?>> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ for (PuttyKeyPairResourceParser<?, ?> p : parsers) {
+ Collection<String> supported = p.getSupportedTypeNames();
+ for (String k : supported) {
+ map.put(k, p);
+ }
+ }
+ DEFAULT_PARSERS = Collections.unmodifiableList(parsers);
+ BY_KEY_TYPE = Collections.unmodifiableNavigableMap(map);
+ DEFAULT_INSTANCE = KeyPairResourceParser.aggregate(parsers);
+ }
+
+ private PuttyKeyUtils() {
+ throw new UnsupportedOperationException("No instance");
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java
new file mode 100644
index 0000000..0a55d55
--- /dev/null
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.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.config.keys.loader.putty;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class RSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<RSAPublicKey, RSAPrivateKey> {
+ public static final RSAPuttyKeyDecoder INSTANCE = new RSAPuttyKeyDecoder();
+
+ public RSAPuttyKeyDecoder() {
+ super(RSAPublicKey.class, RSAPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_RSA));
+ }
+
+ @Override
+ public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
+ throws IOException, GeneralSecurityException {
+ pubReader.skip(); // skip version
+
+ KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
+ BigInteger publicExp = pubReader.readInt();
+ BigInteger modulus = pubReader.readInt();
+ PublicKey pubKey = kf.generatePublic(new RSAPublicKeySpec(modulus, publicExp));
+
+ BigInteger privateExp = prvReader.readInt();
+ BigInteger primeP = prvReader.readInt();
+ BigInteger primeQ = prvReader.readInt();
+ BigInteger crtCoef = prvReader.readInt();
+ BigInteger primeExponentP = privateExp.mod(primeP.subtract(BigInteger.ONE));
+ BigInteger primeExponentQ = privateExp.mod(primeQ.subtract(BigInteger.ONE));
+ RSAPrivateKeySpec prvSpec = new RSAPrivateCrtKeySpec(
+ modulus, publicExp, privateExp, primeP, primeQ, primeExponentP, primeExponentQ, crtCoef);
+ PrivateKey prvKey = kf.generatePrivate(prvSpec);
+ return Collections.singletonList(new KeyPair(pubKey, prvKey));
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/resources/.gitignore
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/resources/.gitignore b/sshd-putty/src/main/resources/.gitignore
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java b/sshd-putty/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java
new file mode 100644
index 0000000..b9f030f
--- /dev/null
+++ b/sshd-putty/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.loader.putty;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.loader.openssh.OpenSSHKeyPairResourceParser;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class PuttyKeyUtilsTest extends JUnitTestSupport {
+ public static final String PASSWORD = "super secret passphrase";
+
+ private final String keyType;
+ private final String regularFile;
+ private final String encryptedFile;
+ private final PuttyKeyPairResourceParser<?, ?> parser;
+
+ public PuttyKeyUtilsTest(String keyType) {
+ this.keyType = keyType;
+ this.parser = PuttyKeyUtils.BY_KEY_TYPE.get(keyType);
+ this.regularFile = getClass().getSimpleName()
+ + "-" + keyType + "-" + KeyPair.class.getSimpleName()
+ + PuttyKeyPairResourceParser.PPK_FILE_SUFFIX;
+ this.encryptedFile = PASSWORD.replace(' ', '-') + "-AES-256-CBC"
+ + "-" + keyType + "-" + KeyPair.class.getSimpleName()
+ + PuttyKeyPairResourceParser.PPK_FILE_SUFFIX;
+ }
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> parameters() {
+ return parameterize(PuttyKeyUtils.BY_KEY_TYPE.keySet());
+ }
+
+ @Test
+ public void testCanDecodePuttyKeyFile() throws IOException, GeneralSecurityException {
+ for (String resource : new String[]{regularFile, encryptedFile}) {
+ URL url = getClass().getResource(resource);
+ if (GenericUtils.isSameReference(regularFile, resource)) {
+ assertNotNull("Missing test resource: " + resource, url);
+ } else {
+ if (url == null) {
+ outputDebugMessage("Skip non-existing encrypted file: %s", resource);
+ continue;
+ }
+ }
+
+ List<String> lines = IoUtils.readAllLines(url);
+ assertTrue(resource + " - can extract key pair", parser.canExtractKeyPairs(resource, lines));
+
+ for (PuttyKeyPairResourceParser<?, ?> other : PuttyKeyUtils.BY_KEY_TYPE.values()) {
+ if (parser == other) {
+ continue;
+ }
+
+ assertFalse(other.getClass().getSimpleName() + "/" + resource + " - unexpected extraction capability",
+ other.canExtractKeyPairs(resource, lines));
+ }
+ }
+ }
+
+ @Test
+ public void testDecodePuttyKeyFile() throws IOException, GeneralSecurityException {
+ URL url = getClass().getResource(regularFile);
+ assertNotNull("Missing test resource: " + regularFile, url);
+
+ Collection<KeyPair> keys = parser.loadKeyPairs(url, null);
+ assertEquals("Mismatched loaded keys count from " + regularFile, 1, GenericUtils.size(keys));
+ assertLoadedKeyPair(regularFile, keys.iterator().next());
+ }
+
+ @Test
+ public void testDecodeEncryptedPuttyKeyFile() throws IOException, GeneralSecurityException {
+ Assume.assumeTrue(BuiltinCiphers.aes256cbc.getTransformation() + " N/A", BuiltinCiphers.aes256cbc.isSupported());
+ URL url = getClass().getResource(encryptedFile);
+ Assume.assumeTrue("Skip non-existent encrypted file: " + encryptedFile, url != null);
+ assertNotNull("Missing test resource: " + encryptedFile, url);
+
+ Collection<KeyPair> keys = parser.loadKeyPairs(url, r -> PASSWORD);
+ assertEquals("Mismatched loaded keys count from " + encryptedFile, 1, GenericUtils.size(keys));
+
+ assertLoadedKeyPair(encryptedFile, keys.iterator().next());
+ }
+
+ private void assertLoadedKeyPair(String prefix, KeyPair kp) throws GeneralSecurityException {
+ assertNotNull(prefix + ": no key pair loaded", kp);
+
+ PublicKey pubKey = kp.getPublic();
+ assertNotNull(prefix + ": no public key loaded", pubKey);
+ assertEquals(prefix + ": mismatched public key type", keyType, KeyUtils.getKeyType(pubKey));
+
+ PrivateKey prvKey = kp.getPrivate();
+ assertNotNull(prefix + ": no private key loaded", prvKey);
+ assertEquals(prefix + ": mismatched private key type", keyType, KeyUtils.getKeyType(prvKey));
+
+ @SuppressWarnings("rawtypes")
+ PrivateKeyEntryDecoder decoder =
+ OpenSSHKeyPairResourceParser.getPrivateKeyEntryDecoder(prvKey);
+ assertNotNull("No private key decoder", decoder);
+
+ if (decoder.isPublicKeyRecoverySupported()) {
+ @SuppressWarnings("unchecked")
+ PublicKey recKey = decoder.recoverPublicKey(prvKey);
+ assertKeyEquals("Mismatched recovered public key", pubKey, recKey);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/.gitignore
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/.gitignore b/sshd-putty/src/test/resources/.gitignore
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/log4j.properties b/sshd-putty/src/test/resources/log4j.properties
new file mode 100644
index 0000000..351467b
--- /dev/null
+++ b/sshd-putty/src/test/resources/log4j.properties
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+#
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+#
+# The logging properties used during tests..
+#
+log4j.rootLogger=INFO, stdout, logfile
+#log4j.logger.org.apache.sshd=TRACE
+#log4j.logger.org.apache.sshd.common.channel.Window=DEBUG
+
+# CONSOLE appender
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+# File appender
+log4j.appender.logfile=org.apache.log4j.FileAppender
+log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
+log4j.appender.logfile.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+log4j.appender.logfile.file=target/sshd-putty-tests.log
+log4j.appender.logfile.append=true
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk
new file mode 100644
index 0000000..509538a
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk
@@ -0,0 +1,10 @@
+PuTTY-User-Key-File-2: ecdsa-sha2-nistp256
+Encryption: none
+Comment: ecdsa-key-20170917
+Public-Lines: 3
+AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM99zj2+E6AN
+xMZ/2SKFP/fAvPfUJUdsgJyn4g7nf36vNpoaRyq1FyHLxyT34AgTl1n3DwcaBXXC
+O5pCv6xFwYk=
+Private-Lines: 1
+AAAAIQDdmu/Dr68r6a0PNbQUN1bX+zS6J5jFsOlEAx8sR73Tuw==
+Private-MAC: 012e0d61593a431ae84beb6216dd29e4b203c1c0
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk
new file mode 100644
index 0000000..238261c
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk
@@ -0,0 +1,11 @@
+PuTTY-User-Key-File-2: ecdsa-sha2-nistp384
+Encryption: none
+Comment: ecdsa-key-20170917
+Public-Lines: 3
+AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNHjIMrdMfXw
+CUqBAhkZw0vXB+qypkiTcL1CmcopmPrKvGHFieFmedeCQotjwJkoAAeb5isZNOXy
+h+7TnHGNrE/pZkHuNwACilpOt659hbhR2OGHX0jdpb8y4RVkuPQssg==
+Private-Lines: 2
+AAAAMHNGt3UPG3evJVl1GRoXXnqTafWLDQdWA2A1tXi8oRW0hhHMRe9/v0SCGL7S
+nL3asg==
+Private-MAC: 6fb6e93559ecacfa468aa5ff9776e4d4283db5ba
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk
new file mode 100644
index 0000000..f60a78e
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk
@@ -0,0 +1,12 @@
+PuTTY-User-Key-File-2: ecdsa-sha2-nistp521
+Encryption: none
+Comment: ecdsa-key-20170917
+Public-Lines: 4
+AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADGM237T9rT
+zE++sOFDN0VWfYfojlQ8dYP82OlgA24Yh0ZpOsezBBiHtHfMHl9tWHmch1YKmH7B
+lOfqbOgcIhz9cwA2V7Nu3IUGqxZT18LOXEpcdyDSphJ6jsy1urqBLrOz4DF6Udyr
+rFV4OQELovf8YdUsM91YPfe1DfnSRi1I5v20uA==
+Private-Lines: 2
+AAAAQgE28iZoHARx+2VzL7Y45FaY44TngX2b4StlC8wOlYF7NY/ba+Pt2RT/WcNL
+ytmLdj5QeV/fFJ9x8i00mTU6KCF2AA==
+Private-MAC: 7379e9986066087dff9339d2b0b968c2b31f45c7
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk
new file mode 100644
index 0000000..59a8fac
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk
@@ -0,0 +1,17 @@
+PuTTY-User-Key-File-2: ssh-dss
+Encryption: none
+Comment: dsa-key-20130709
+Public-Lines: 10
+AAAAB3NzaC1kc3MAAACBAMg/IxsG5BxnF5gM7IKqqR0rftxZC+n5GlbO+J4H+iIb
+/KR8NBehkxG3CrBZMF96M2K1sEGYLob+3k4r71oWaPul8n5rt9kpd+JSq4iD2ygO
+yg6Kd1/YDBHoxneizy6I/bGsLwhAAKWcRNrXmYVKGzhrhvZWN12AJDq2mGdj3szL
+AAAAFQD7a2MltdUSF7FU3//SpW4WGjZbeQAAAIBf0nNsfKQL/TEMo7IpTrEMg5V0
+RnSigCX0+yUERS42GW/ZeCZBJw7oL2XZbuBtu63vMjDgVpnb92BdrcPgjJ7EFW6D
+lcyeuywStmg1ygXmDR2AQCxv0eX2CQgrdUczmRa155SDVUTvTQlO1IyKx0vwKAh1
+H7E3yJUfkTAJstbGYQAAAIEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJV
+c64kFs4yx+abYpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4
+dVbQZsdt+SLaXWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3
+T7A=
+Private-Lines: 1
+AAAAFQCE6flGnmVCAbzo9YsbdJWBnxMnBA==
+Private-MAC: 6ace0a5e5bf23649c1375e91dcd71d1def6c6aa1
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk
new file mode 100644
index 0000000..614ac69
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk
@@ -0,0 +1,9 @@
+PuTTY-User-Key-File-2: ssh-ed25519
+Encryption: none
+Comment: ed25519-key-20170917
+Public-Lines: 2
+AAAAC3NzaC1lZDI1NTE5AAAAIN7fuKSIM5TbAX/1I1Ts3tfyo5eEs7JpmKsegHs/
+9fIi
+Private-Lines: 1
+AAAAIADKJJPxsUp7JXLzm1zwk8UswW/lkiwPJ73CbqGvalgP
+Private-MAC: 28a22234152feaf1d9a6a10ca0ae3a51b5e6dd52
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk
new file mode 100644
index 0000000..da2868e
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk
@@ -0,0 +1,18 @@
+PuTTY-User-Key-File-2: ssh-rsa
+Encryption: none
+Comment: rsa-key-20130709
+Public-Lines: 4
+AAAAB3NzaC1yc2EAAAABJQAAAIBLSj7+SllQdWjkD8EN4sA/fUv/jhc+ssGmCYx3
+uoiPMxjKH3xUPWu4zxJzhdlgFy4gchzjU51fDS4Oel6xGoWbGKGe4ZLQdE/t8N8l
+jAfOm/5lGp5tFhHs9UHoSm/h3RsErWNjKPjTGIlID35IcOXVhfhp9fX0RU6y/ZBI
+PhM20w==
+Private-Lines: 8
+AAAAgBx89T2fl2qNSkh0qemUEWQhkmCyTfwMSUW+bIBUawXATpGrDHLmztBOWgIy
+pUb0A52S9iyAgLwugCEnYhl/qCxvoARH7ZyTdYAL4KjJDySxVuqeo/ZhLscYcMAz
+MOyn8g5cR4dRgEwJ1/pRuK8r4+Z96zJG4NlxlHsUjHuj7t1dAAAAQQCTrj48XKIX
+M3dxWLSsSXbUCOpmAOTviuz9LD0H1ik7a6ebr0P6GTl9z7iscBgzdjBIHMFcdvar
+ophUJ5iRanCvAAAAQQCCg1VU1H5FHMipRvvw8b/zRqDsx6GTZs03ffhyKrTl1Dcd
+0oKy5/U3kQwdXPOSlVZeyX8nUVE2o7DOh7INsX0dAAAAQHkPjxivrN0SQuVAx+tK
+uRJ8vGL7sBOZ9gdTS25T3BVEkhRt37aDcshrodzDCzd515cwhmbLSsOsgyxcTwcX
+7SA=
+Private-MAC: 2416438f1a7ebdd33d519f6102d843b5f2c565d4
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk
new file mode 100644
index 0000000..4c601c7
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk
@@ -0,0 +1,10 @@
+PuTTY-User-Key-File-2: ecdsa-sha2-nistp256
+Encryption: aes256-cbc
+Comment: ecdsa-key-20170917
+Public-Lines: 3
+AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM99zj2+E6AN
+xMZ/2SKFP/fAvPfUJUdsgJyn4g7nf36vNpoaRyq1FyHLxyT34AgTl1n3DwcaBXXC
+O5pCv6xFwYk=
+Private-Lines: 1
+/8MdniIqAaST5t3/bRx4mFdFxqN8jIwI0Hh7VTy8IV143j+PktgGIoHsUwTiEujQ
+Private-MAC: be4b37d4b65ad09e6890534a2ba355599da796c6
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk
new file mode 100644
index 0000000..f57f5fd
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk
@@ -0,0 +1,11 @@
+PuTTY-User-Key-File-2: ecdsa-sha2-nistp384
+Encryption: aes256-cbc
+Comment: ecdsa-key-20170917
+Public-Lines: 3
+AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNHjIMrdMfXw
+CUqBAhkZw0vXB+qypkiTcL1CmcopmPrKvGHFieFmedeCQotjwJkoAAeb5isZNOXy
+h+7TnHGNrE/pZkHuNwACilpOt659hbhR2OGHX0jdpb8y4RVkuPQssg==
+Private-Lines: 2
+MOgJnSZ8ZqGHFVtQvYKpOyTGWjMVIjIOMIUwhbxBuTiQ7WEyNPn9jjTsSwXtJxrG
+UI6NGeIqZ41P2e8JINhMIg==
+Private-MAC: 5ec40946270150ddfca35cce61f4265d7bfe7b7f
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk
new file mode 100644
index 0000000..97077fd
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk
@@ -0,0 +1,12 @@
+PuTTY-User-Key-File-2: ecdsa-sha2-nistp521
+Encryption: aes256-cbc
+Comment: ecdsa-key-20170917
+Public-Lines: 4
+AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADGM237T9rT
+zE++sOFDN0VWfYfojlQ8dYP82OlgA24Yh0ZpOsezBBiHtHfMHl9tWHmch1YKmH7B
+lOfqbOgcIhz9cwA2V7Nu3IUGqxZT18LOXEpcdyDSphJ6jsy1urqBLrOz4DF6Udyr
+rFV4OQELovf8YdUsM91YPfe1DfnSRi1I5v20uA==
+Private-Lines: 2
++44AQO4aflPquBZbdB3UtdLuXuHV2u1YoghxYXPFGdhskMt+XjJhUlrOHNX8rmgR
+E9nni474zGw9ni/3LIZwMILQI/xTfiQm3t6nKFV8nyI=
+Private-MAC: 99c25e348a84de4876163758ad13b2ad1dc43629
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk
new file mode 100644
index 0000000..9da31f1
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk
@@ -0,0 +1,17 @@
+PuTTY-User-Key-File-2: ssh-dss
+Encryption: aes256-cbc
+Comment: dsa-key-20130909
+Public-Lines: 10
+AAAAB3NzaC1kc3MAAACBALVdprWfZ7+FiITCILnDkeMZ2ntkV2WjW5RcyiQvJvBO
+jCNiVtK87xATEOfBb20YvNZ/CibBjGS1TL5TBqRV5XleucPHMJZ5rXdJ2FH5oZnL
+kna3Et+L1/O/GQMmp2vfSFrO3n3+mI1Jozx3FoQO8jr1zIerJ5Mc4LKqsIQB9hvR
+AAAAFQD+z1y1/4ll4ax3rri8mkYgGDhqIQAAAIBVU4VJ7V7GoEQJ5WBMbpDEcLIZ
+KUgSHsJMQzWnLOi/DcsPjVMDX6FWGPLtrjd7fgInlPMCC/SPAhXdaXMvHZSkvBHV
+DfNjpsDgsxBnK1FKqRGtD49rETFGDl92EOsyBhv+9ymdOPX6R0hCqS+ulZheQPXI
+iHXdIvQK2Ev5Dy3xNgAAAIA8qhumHZcKss+Puuw+mY5J5Qt7Omv16MuDsYiVqrBq
+1V2C9gutx3Tu+n5QYi0xPlkkP/knMtkUZS+Wt3Dr8zPcEzNBc/Tm2EdYp11jZNx4
+4PM4ing+aCU5oGcg/7RS5CrY5Fn/rvgHqK22XiC8/U55iti44bWKvI6HCejExeZX
+iA==
+Private-Lines: 1
+64IdcIX48CNGjhcxsVN0vSNpT7S72e3FVdQ3t4ENAvI=
+Private-MAC: 8e62b44b5c080965e361936520be4feaa50285b1
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk
new file mode 100644
index 0000000..668ef1e
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk
@@ -0,0 +1,9 @@
+PuTTY-User-Key-File-2: ssh-ed25519
+Encryption: aes256-cbc
+Comment: ed25519-key-20170917
+Public-Lines: 2
+AAAAC3NzaC1lZDI1NTE5AAAAIN7fuKSIM5TbAX/1I1Ts3tfyo5eEs7JpmKsegHs/
+9fIi
+Private-Lines: 1
+0cPG5BR80jQcJmHKs6IjpHS3R4/CTnudnJB4BcjaqKlRk0l603GVMDzTxkaICCb8
+Private-MAC: 381cff136b2516331ff4511cf382533fc14f0aeb
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk
new file mode 100644
index 0000000..2a11d04
--- /dev/null
+++ b/sshd-putty/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk
@@ -0,0 +1,18 @@
+PuTTY-User-Key-File-2: ssh-rsa
+Encryption: aes256-cbc
+Comment: rsa-key-20130909
+Public-Lines: 4
+AAAAB3NzaC1yc2EAAAABJQAAAIEAhY5eZJrX0a3+rtEZCq0nu2zvAHp16nk93jhi
+p7c9tTDlGm9QEAgqzmuilEeUQ4BssxAvhCFEo/7Qbg4M7PwcA5cFkjXE4gj0YDJM
+ay7l2mb5aIoS/hACgNz54p/w/UgfQC1Vygt6QtvXXAW8Lh/YCN4Zw4ViROUhoYuy
+3K5SBYs=
+Private-Lines: 8
+mqcGPnrv9d1tYkJZSGaCy5REslPZ2xh8m7qAbN+bD1m7iQ77pLxlKyzs82rbRaC9
+KSnKwsbFl7o92NT+9yYKJ7ehXyWyrUXkn9KcPk7MzNVwMuWVDXwvHodGLCyVCLYq
+PNipvg2USHvnCjnnvtMysBRNJiHTMOaf/gSZLyaEuznYo3FEClMPzggY9b2nrxnV
+O1ttk1FJatkRflwFjn3A/R/GpowmBnkDyCkVlTvR+uBAg8iIy1Vzj5zIV9zmzfgx
+DxPot+Y81y+Xe3ohVh2s1FVvLw+KQbYbCQam5j0V/dTQ+oVWjCJBlibD3aVTGK0M
+Jswz8wPwXFo5N0yX/6ZTrshbvTzoO1bg0+HUu581ZSAeqttk9C1RLmWFS8YDm0Hn
+GhDXrjuAvKJ3cjeVJsumgVw45NYGARuzV24TlHUtU+eze8Y/0NsPJXoCfVoYjTjb
+fjlMh9rYbRdyNHXwYTzwbw==
+Private-MAC: f4c50b3da0b73c34e8989411fc48c884c09e20a0
[51/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
[SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/10de190e
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/10de190e
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/10de190e
Branch: refs/heads/master
Commit: 10de190e7d3f9189deb76b8d08c72334a1fe2df0
Parents: 1e82547
Author: Goldstein Lyor <ly...@cb4.com>
Authored: Wed Sep 5 18:05:53 2018 +0300
Committer: Lyor Goldstein <ly...@gmail.com>
Committed: Thu Sep 6 19:07:54 2018 +0300
----------------------------------------------------------------------
assembly/pom.xml | 5 +
pom.xml | 1 +
sshd-cli/pom.xml | 7 +
.../java/org/apache/sshd/cli/CliSupport.java | 6 +-
.../sshd/cli/client/SshClientCliSupport.java | 16 +-
.../apache/sshd/cli/client/SshKeyScanMain.java | 4 +-
.../sshd/cli/server/SshServerCliSupport.java | 4 +-
.../apache/sshd/cli/server/SshServerMain.java | 5 +-
.../apache/sshd/cli/server/SshFsMounter.java | 11 +-
sshd-common/pom.xml | 128 ++
.../org/apache/sshd/sshd-version.properties | 23 +
.../auth/AuthenticationIdentitiesProvider.java | 125 ++
.../auth/hostbased/HostKeyIdentityProvider.java | 53 +
.../auth/password/PasswordIdentityProvider.java | 169 +++
.../client/auth/pubkey/PublicKeyIdentity.java | 42 +
.../hosts/ConfigFileHostEntryResolver.java | 107 ++
.../DefaultConfigFileHostEntryResolver.java | 95 ++
.../client/config/hosts/HostConfigEntry.java | 1169 ++++++++++++++++++
.../config/hosts/HostConfigEntryResolver.java | 60 +
.../client/config/hosts/HostPatternValue.java | 97 ++
.../client/config/hosts/HostPatternsHolder.java | 343 +++++
.../client/config/hosts/KnownHostDigest.java | 66 +
.../client/config/hosts/KnownHostEntry.java | 276 +++++
.../client/config/hosts/KnownHostHashValue.java | 170 +++
.../keys/BuiltinClientIdentitiesWatcher.java | 106 ++
.../config/keys/ClientIdentitiesWatcher.java | 139 +++
.../sshd/client/config/keys/ClientIdentity.java | 268 ++++
.../config/keys/ClientIdentityFileWatcher.java | 141 +++
.../config/keys/ClientIdentityLoader.java | 95 ++
.../config/keys/ClientIdentityProvider.java | 42 +
.../keys/DefaultClientIdentitiesWatcher.java | 66 +
.../org/apache/sshd/common/AttributeStore.java | 97 ++
.../org/apache/sshd/common/BuiltinFactory.java | 40 +
.../java/org/apache/sshd/common/Closeable.java | 126 ++
.../java/org/apache/sshd/common/Factory.java | 41 +
.../org/apache/sshd/common/NamedFactory.java | 66 +
.../org/apache/sshd/common/NamedResource.java | 104 ++
.../org/apache/sshd/common/OptionalFeature.java | 92 ++
.../apache/sshd/common/PropertyResolver.java | 124 ++
.../sshd/common/PropertyResolverUtils.java | 482 ++++++++
.../apache/sshd/common/RuntimeSshException.java | 48 +
.../org/apache/sshd/common/SshConstants.java | 245 ++++
.../org/apache/sshd/common/SshException.java | 72 ++
.../apache/sshd/common/SyspropsMapWrapper.java | 209 ++++
.../sshd/common/auth/MutableUserHolder.java | 27 +
.../apache/sshd/common/auth/UsernameHolder.java | 32 +
.../apache/sshd/common/cipher/BaseCipher.java | 113 ++
.../sshd/common/cipher/BaseRC4Cipher.java | 54 +
.../sshd/common/cipher/BuiltinCiphers.java | 348 ++++++
.../org/apache/sshd/common/cipher/Cipher.java | 89 ++
.../sshd/common/cipher/CipherFactory.java | 31 +
.../sshd/common/cipher/CipherInformation.java | 45 +
.../apache/sshd/common/cipher/CipherNone.java | 63 +
.../org/apache/sshd/common/cipher/ECCurves.java | 580 +++++++++
.../org/apache/sshd/common/cipher/package.html | 26 +
.../common/compression/BaseCompression.java | 48 +
.../common/compression/BuiltinCompressions.java | 239 ++++
.../sshd/common/compression/Compression.java | 71 ++
.../compression/CompressionDelayedZlib.java | 40 +
.../common/compression/CompressionFactory.java | 31 +
.../compression/CompressionInformation.java | 42 +
.../common/compression/CompressionNone.java | 76 ++
.../common/compression/CompressionZlib.java | 85 ++
.../apache/sshd/common/compression/package.html | 25 +
.../common/config/CompressionConfigValue.java | 94 ++
.../common/config/ConfigFileReaderSupport.java | 230 ++++
.../common/config/FactoriesListParseResult.java | 51 +
.../sshd/common/config/ListParseResult.java | 66 +
.../sshd/common/config/LogLevelValue.java | 56 +
.../config/NamedFactoriesListParseResult.java | 47 +
.../config/NamedResourceListParseResult.java | 57 +
.../sshd/common/config/SyslogFacilityValue.java | 51 +
.../sshd/common/config/TimeValueConfig.java | 180 +++
.../sshd/common/config/VersionProperties.java | 98 ++
.../common/config/keys/AuthorizedKeyEntry.java | 480 +++++++
.../common/config/keys/BuiltinIdentities.java | 212 ++++
.../config/keys/FilePasswordProvider.java | 45 +
.../sshd/common/config/keys/Identity.java | 42 +
.../config/keys/IdentityResourceLoader.java | 49 +
.../sshd/common/config/keys/IdentityUtils.java | 159 +++
.../common/config/keys/KeyEntryResolver.java | 190 +++
.../sshd/common/config/keys/KeyRandomArt.java | 310 +++++
.../sshd/common/config/keys/KeyUtils.java | 937 ++++++++++++++
.../config/keys/PrivateKeyEntryDecoder.java | 142 +++
.../config/keys/PrivateKeyEntryResolver.java | 70 ++
.../sshd/common/config/keys/PublicKeyEntry.java | 286 +++++
.../config/keys/PublicKeyEntryDecoder.java | 114 ++
.../config/keys/PublicKeyEntryResolver.java | 70 ++
.../impl/AbstractIdentityResourceLoader.java | 62 +
.../keys/impl/AbstractKeyEntryResolver.java | 59 +
.../impl/AbstractPrivateKeyEntryDecoder.java | 40 +
.../impl/AbstractPublicKeyEntryDecoder.java | 41 +
.../keys/impl/DSSPublicKeyEntryDecoder.java | 119 ++
.../keys/impl/ECDSAPublicKeyEntryDecoder.java | 178 +++
.../config/keys/impl/RSAPublicKeyDecoder.java | 117 ++
.../keys/loader/AESPrivateKeyObfuscator.java | 110 ++
.../loader/AbstractKeyPairResourceParser.java | 181 +++
.../loader/AbstractPrivateKeyObfuscator.java | 191 +++
.../keys/loader/DESPrivateKeyObfuscator.java | 76 ++
.../keys/loader/KeyPairResourceLoader.java | 129 ++
.../keys/loader/KeyPairResourceParser.java | 192 +++
.../loader/PrivateKeyEncryptionContext.java | 270 ++++
.../keys/loader/PrivateKeyObfuscator.java | 64 +
.../OpenSSHDSSPrivateKeyEntryDecoder.java | 139 +++
.../OpenSSHECDSAPrivateKeyEntryDecoder.java | 164 +++
.../openssh/OpenSSHKeyPairResourceParser.java | 358 ++++++
.../loader/openssh/OpenSSHParserContext.java | 83 ++
.../openssh/OpenSSHRSAPrivateKeyDecoder.java | 135 ++
.../pem/AbstractPEMResourceKeyPairParser.java | 167 +++
.../loader/pem/DSSPEMResourceKeyPairParser.java | 126 ++
.../pem/ECDSAPEMResourceKeyPairParser.java | 220 ++++
.../loader/pem/KeyPairPEMResourceParser.java | 37 +
.../keys/loader/pem/PEMResourceParserUtils.java | 109 ++
.../pem/PKCS8PEMResourceKeyPairParser.java | 156 +++
.../loader/pem/RSAPEMResourceKeyPairParser.java | 142 +++
.../apache/sshd/common/digest/BaseDigest.java | 156 +++
.../sshd/common/digest/BuiltinDigests.java | 166 +++
.../org/apache/sshd/common/digest/Digest.java | 36 +
.../sshd/common/digest/DigestFactory.java | 32 +
.../sshd/common/digest/DigestInformation.java | 36 +
.../apache/sshd/common/digest/DigestUtils.java | 228 ++++
.../org/apache/sshd/common/digest/package.html | 25 +
.../sshd/common/future/AbstractSshFuture.java | 194 +++
.../apache/sshd/common/future/CloseFuture.java | 40 +
.../sshd/common/future/DefaultCloseFuture.java | 52 +
.../sshd/common/future/DefaultSshFuture.java | 236 ++++
.../future/DefaultVerifiableSshFuture.java | 30 +
.../apache/sshd/common/future/SshFuture.java | 48 +
.../sshd/common/future/SshFutureListener.java | 42 +
.../sshd/common/future/VerifiableFuture.java | 68 +
.../sshd/common/future/WaitableFuture.java | 118 ++
.../keyprovider/AbstractKeyPairProvider.java | 32 +
.../AbstractResourceKeyPairProvider.java | 234 ++++
.../ClassLoadableResourceKeyPairProvider.java | 113 ++
.../common/keyprovider/FileKeyPairProvider.java | 92 ++
.../common/keyprovider/KeyIdentityProvider.java | 170 +++
.../common/keyprovider/KeyPairProvider.java | 181 +++
.../keyprovider/KeyPairProviderHolder.java | 35 +
.../keyprovider/MappedKeyPairProvider.java | 97 ++
.../org/apache/sshd/common/mac/BaseMac.java | 111 ++
.../org/apache/sshd/common/mac/BuiltinMacs.java | 273 ++++
.../java/org/apache/sshd/common/mac/Mac.java | 52 +
.../org/apache/sshd/common/mac/MacFactory.java | 31 +
.../apache/sshd/common/mac/MacInformation.java | 41 +
.../org/apache/sshd/common/mac/package.html | 25 +
.../sshd/common/random/AbstractRandom.java | 34 +
.../common/random/AbstractRandomFactory.java | 43 +
.../apache/sshd/common/random/JceRandom.java | 60 +
.../sshd/common/random/JceRandomFactory.java | 42 +
.../org/apache/sshd/common/random/Random.java | 56 +
.../sshd/common/random/RandomFactory.java | 31 +
.../common/random/SingletonRandomFactory.java | 70 ++
.../org/apache/sshd/common/random/package.html | 25 +
.../common/signature/AbstractSignature.java | 149 +++
.../common/signature/BuiltinSignatures.java | 305 +++++
.../apache/sshd/common/signature/Signature.java | 88 ++
.../sshd/common/signature/SignatureDSA.java | 142 +++
.../sshd/common/signature/SignatureECDSA.java | 144 +++
.../signature/SignatureFactoriesManager.java | 94 ++
.../sshd/common/signature/SignatureFactory.java | 31 +
.../sshd/common/signature/SignatureRSA.java | 89 ++
.../apache/sshd/common/signature/package.html | 25 +
.../sshd/common/util/EventListenerUtils.java | 212 ++++
.../apache/sshd/common/util/EventNotifier.java | 35 +
.../apache/sshd/common/util/GenericUtils.java | 915 ++++++++++++++
.../sshd/common/util/IgnoringEmptyMap.java | 128 ++
.../sshd/common/util/Int2IntFunction.java | 66 +
.../org/apache/sshd/common/util/Invoker.java | 116 ++
.../apache/sshd/common/util/MapEntryUtils.java | 51 +
.../apache/sshd/common/util/NumberUtils.java | 310 +++++
.../apache/sshd/common/util/ObjectBuilder.java | 38 +
.../org/apache/sshd/common/util/OsUtils.java | 257 ++++
.../org/apache/sshd/common/util/Readable.java | 53 +
.../sshd/common/util/ReflectionUtils.java | 53 +
.../apache/sshd/common/util/SelectorUtils.java | 805 ++++++++++++
.../sshd/common/util/SshdEventListener.java | 44 +
.../apache/sshd/common/util/ValidateUtils.java | 216 ++++
.../apache/sshd/common/util/VersionInfo.java | 137 ++
.../apache/sshd/common/util/buffer/Buffer.java | 798 ++++++++++++
.../common/util/buffer/BufferException.java | 30 +
.../sshd/common/util/buffer/BufferUtils.java | 604 +++++++++
.../common/util/buffer/ByteArrayBuffer.java | 253 ++++
.../keys/AbstractBufferPublicKeyParser.java | 88 ++
.../util/buffer/keys/BufferPublicKeyParser.java | 111 ++
.../buffer/keys/DSSBufferPublicKeyParser.java | 52 +
.../buffer/keys/ECBufferPublicKeyParser.java | 81 ++
.../keys/ED25519BufferPublicKeyParser.java | 47 +
.../buffer/keys/RSABufferPublicKeyParser.java | 49 +
.../util/closeable/AbstractCloseable.java | 162 +++
.../util/closeable/AbstractInnerCloseable.java | 48 +
.../sshd/common/util/closeable/Builder.java | 115 ++
.../common/util/closeable/FuturesCloseable.java | 76 ++
.../common/util/closeable/IoBaseCloseable.java | 35 +
.../util/closeable/ParallelCloseable.java | 73 ++
.../util/closeable/SequentialCloseable.java | 71 ++
.../common/util/closeable/SimpleCloseable.java | 71 ++
.../util/io/CloseableEmptyInputStream.java | 96 ++
.../sshd/common/util/io/DirectoryScanner.java | 380 ++++++
.../sshd/common/util/io/EmptyInputStream.java | 66 +
.../sshd/common/util/io/FileInfoExtractor.java | 53 +
.../common/util/io/InputStreamWithChannel.java | 32 +
.../org/apache/sshd/common/util/io/IoUtils.java | 556 +++++++++
.../sshd/common/util/io/LimitInputStream.java | 113 ++
.../util/io/LoggingFilterOutputStream.java | 67 +
.../common/util/io/ModifiableFileWatcher.java | 258 ++++
.../sshd/common/util/io/NoCloseInputStream.java | 47 +
.../common/util/io/NoCloseOutputStream.java | 47 +
.../sshd/common/util/io/NoCloseReader.java | 46 +
.../sshd/common/util/io/NoCloseWriter.java | 46 +
.../sshd/common/util/io/NullInputStream.java | 90 ++
.../sshd/common/util/io/NullOutputStream.java | 72 ++
.../common/util/io/OutputStreamWithChannel.java | 32 +
.../sshd/common/util/io/der/ASN1Class.java | 93 ++
.../sshd/common/util/io/der/ASN1Object.java | 338 +++++
.../sshd/common/util/io/der/ASN1Type.java | 118 ++
.../sshd/common/util/io/der/DERParser.java | 151 +++
.../sshd/common/util/io/der/DERWriter.java | 172 +++
.../common/util/io/functors/IOFunction.java | 86 ++
.../util/logging/AbstractLoggingBean.java | 72 ++
.../sshd/common/util/logging/LoggingUtils.java | 549 ++++++++
.../sshd/common/util/logging/SimplifiedLog.java | 55 +
.../sshd/common/util/net/NetworkConnector.java | 90 ++
.../sshd/common/util/net/SshdSocketAddress.java | 639 ++++++++++
.../AbstractSecurityProviderRegistrar.java | 129 ++
.../util/security/SecurityEntityFactory.java | 194 +++
.../util/security/SecurityProviderChoice.java | 130 ++
.../security/SecurityProviderRegistrar.java | 337 +++++
.../common/util/security/SecurityUtils.java | 759 ++++++++++++
.../BouncyCastleGeneratorHostKeyProvider.java | 48 +
.../BouncyCastleKeyPairResourceParser.java | 130 ++
.../bouncycastle/BouncyCastleRandom.java | 88 ++
.../bouncycastle/BouncyCastleRandomFactory.java | 45 +
.../BouncyCastleSecurityProviderRegistrar.java | 128 ++
.../security/eddsa/Ed25519PublicKeyDecoder.java | 97 ++
.../eddsa/EdDSASecurityProviderRegistrar.java | 104 ++
.../eddsa/EdDSASecurityProviderUtils.java | 201 +++
.../OpenSSHEd25519PrivateKeyEntryDecoder.java | 172 +++
.../util/security/eddsa/SignatureEd25519.java | 49 +
.../util/threads/CloseableExecutorService.java | 28 +
.../util/threads/ExecutorServiceCarrier.java | 31 +
.../common/util/threads/NoCloseExecutor.java | 160 +++
.../util/threads/SshThreadPoolExecutor.java | 138 +++
.../common/util/threads/SshdThreadFactory.java | 78 ++
.../sshd/common/util/threads/ThreadUtils.java | 185 +++
.../AbstractGeneratorHostKeyProvider.java | 293 +++++
.../SimpleGeneratorHostKeyProvider.java | 67 +
.../password/PasswordIdentityProviderTest.java | 72 ++
.../hosts/ConfigFileHostEntryResolverTest.java | 139 +++
.../config/hosts/HostConfigEntryTest.java | 325 +++++
.../config/hosts/KnownHostHashValueTest.java | 79 ++
.../BuiltinClientIdentitiesWatcherTest.java | 161 +++
.../keys/ClientIdentityFileWatcherTest.java | 121 ++
.../client/config/keys/ClientIdentityTest.java | 100 ++
.../apache/sshd/common/SshConstantsTest.java | 75 ++
.../sshd/common/VersionPropertiesTest.java | 45 +
.../sshd/common/cipher/AES192CTRTest.java | 41 +
.../sshd/common/cipher/AES256CBCTest.java | 41 +
.../sshd/common/cipher/ARCFOUR128Test.java | 39 +
.../sshd/common/cipher/ARCFOUR256Test.java | 41 +
.../sshd/common/cipher/BaseCipherTest.java | 95 ++
.../apache/sshd/common/cipher/ECCurvesTest.java | 107 ++
.../compression/BuiltinCompressionsTest.java | 171 +++
.../sshd/common/config/TimeValueConfigTest.java | 57 +
...AuthorizedKeyEntryLoginOptionsParseTest.java | 124 ++
.../config/keys/BuiltinIdentitiesTest.java | 116 ++
.../common/config/keys/KeyRandomArtTest.java | 117 ++
.../common/config/keys/KeyUtilsCloneTest.java | 120 ++
.../KeyUtilsFingerprintCaseSensitivityTest.java | 96 ++
.../keys/KeyUtilsFingerprintGenerationTest.java | 159 +++
.../sshd/common/config/keys/KeyUtilsTest.java | 157 +++
.../common/config/keys/PublicKeyEntryTest.java | 62 +
.../loader/AESPrivateKeyObfuscatorTest.java | 79 ++
.../OpenSSHKeyPairResourceParserTest.java | 108 ++
.../pem/PKCS8PEMResourceKeyPairParserTest.java | 107 ++
.../sshd/common/digest/BuiltinDigestsTest.java | 65 +
.../common/future/DefaultSshFutureTest.java | 137 ++
.../common/keyprovider/KeyPairProviderTest.java | 79 ++
.../apache/sshd/common/mac/BuiltinMacsTest.java | 166 +++
.../apache/sshd/common/mac/MacVectorsTest.java | 311 +++++
.../sshd/common/random/RandomFactoryTest.java | 83 ++
.../common/signature/BuiltinSignaturesTest.java | 149 +++
.../sshd/common/signature/SignatureDSATest.java | 109 ++
.../sshd/common/signature/SignatureRSATest.java | 119 ++
.../common/signature/SignaturesDevelopment.java | 81 ++
.../common/util/EventListenerUtilsTest.java | 149 +++
.../sshd/common/util/GenericUtilsTest.java | 173 +++
.../sshd/common/util/Int2IntFunctionTest.java | 154 +++
.../sshd/common/util/NumberUtilsTest.java | 77 ++
.../apache/sshd/common/util/OsUtilsTest.java | 135 ++
.../sshd/common/util/SelectorUtilsTest.java | 147 +++
.../sshd/common/util/ThreadUtilsTest.java | 72 ++
.../sshd/common/util/ValidateUtilsTest.java | 44 +
.../sshd/common/util/VersionInfoTest.java | 52 +
.../sshd/common/util/buffer/BufferTest.java | 103 ++
.../common/util/buffer/BufferUtilsTest.java | 73 ++
.../util/closeable/CloseableUtilsTest.java | 163 +++
.../common/util/io/EmptyInputStreamTest.java | 120 ++
.../apache/sshd/common/util/io/IoUtilsTest.java | 61 +
.../common/util/io/LimitInputStreamTest.java | 118 ++
.../util/io/ModifiableFileWatcherTest.java | 82 ++
.../common/util/io/NoCloseInputStreamTest.java | 89 ++
.../common/util/io/NoCloseOutputStreamTest.java | 69 ++
.../sshd/common/util/io/NoCloseReaderTest.java | 95 ++
.../sshd/common/util/io/NoCloseWriterTest.java | 73 ++
.../common/util/io/NullInputStreamTest.java | 118 ++
.../common/util/io/NullOutputStreamTest.java | 80 ++
.../sshd/common/util/io/der/ASN1ClassTest.java | 67 +
.../sshd/common/util/io/der/ASN1TypeTest.java | 67 +
.../sshd/common/util/io/der/DERParserTest.java | 61 +
.../sshd/common/util/io/der/DERWriterTest.java | 64 +
.../util/net/SshdSocketIpv6AddressTest.java | 88 ++
...SecurityProviderRegistrarCipherNameTest.java | 75 ++
.../SecurityProviderRegistrarTestSupport.java | 62 +
.../common/util/security/SecurityUtilsTest.java | 231 ++++
.../util/security/eddsa/EDDSAProviderTest.java | 126 ++
.../util/security/eddsa/Ed25519VectorsTest.java | 238 ++++
.../EdDSASecurityProviderRegistrarTest.java | 85 ++
.../AbstractGeneratorHostKeyProviderTest.java | 83 ++
.../PEMGeneratorHostKeyProviderTest.java | 141 +++
.../SimpleGeneratorHostKeyProviderTest.java | 133 ++
.../sshd/util/test/CommonTestSupportUtils.java | 620 ++++++++++
.../test/JUnit4ClassRunnerWithParameters.java | 48 +
.../JUnit4ClassRunnerWithParametersFactory.java | 58 +
.../test/JUnit4SingleInstanceClassRunner.java | 54 +
.../apache/sshd/util/test/JUnitTestSupport.java | 572 +++++++++
.../org/apache/sshd/util/test/NoIoTestCase.java | 30 +
.../test/OutputCountTrackingOutputStream.java | 56 +
.../apache/sshd/util/test/TeeOutputStream.java | 64 +
sshd-common/src/test/resources/log4j.properties | 38 +
.../testReadGlobalHostsConfigEntries.config.txt | 22 +
.../testReadMultipleHostPatterns.config.txt | 5 +
.../testReadSimpleHostsConfigEntries.config.txt | 8 +
.../org/apache/sshd/client/config/keys/id_dsa | 12 +
.../org/apache/sshd/client/config/keys/id_ecdsa | 5 +
.../org/apache/sshd/client/config/keys/id_rsa | 27 +
...OpenSSHKeyPairResourceParserTest-DSA-KeyPair | 21 +
...SSHKeyPairResourceParserTest-DSA-KeyPair.pub | 1 +
...enSSHKeyPairResourceParserTest-ECDSA-KeyPair | 12 +
...HKeyPairResourceParserTest-ECDSA-KeyPair.pub | 1 +
...SSHKeyPairResourceParserTest-ED25519-KeyPair | 7 +
...eyPairResourceParserTest-ED25519-KeyPair.pub | 1 +
...OpenSSHKeyPairResourceParserTest-RSA-KeyPair | 49 +
...SSHKeyPairResourceParserTest-RSA-KeyPair.pub | 1 +
.../EDDSAProviderTest-EDDSA-OpenSSH-KeyPair | 7 +
.../EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub | 1 +
.../util/security/SecurityUtilsTest-DSA-KeyPair | 12 +
.../security/SecurityUtilsTest-DSA-KeyPair.pub | 1 +
.../security/SecurityUtilsTest-EC-256-KeyPair | 5 +
.../SecurityUtilsTest-EC-256-KeyPair.pub | 1 +
.../security/SecurityUtilsTest-EC-384-KeyPair | 6 +
.../SecurityUtilsTest-EC-384-KeyPair.pub | 1 +
.../security/SecurityUtilsTest-EC-521-KeyPair | 7 +
.../SecurityUtilsTest-EC-521-KeyPair.pub | 1 +
.../util/security/SecurityUtilsTest-RSA-KeyPair | 27 +
.../security/SecurityUtilsTest-RSA-KeyPair.pub | 1 +
.../super-secret-passphrase-RSA-AES-128-key | 30 +
.../super-secret-passphrase-RSA-AES-128-key.pub | 1 +
.../super-secret-passphrase-RSA-AES-192-key | 30 +
.../super-secret-passphrase-RSA-AES-192-key.pub | 1 +
.../super-secret-passphrase-RSA-AES-256-key | 30 +
.../super-secret-passphrase-RSA-AES-256-key.pub | 1 +
.../super-secret-passphrase-RSA-DES-EDE3-key | 30 +
...super-secret-passphrase-RSA-DES-EDE3-key.pub | 1 +
sshd-contrib/pom.xml | 9 +-
...SimpleAccessControlScpEventListenerTest.java | 8 +-
...impleAccessControlSftpEventListenerTest.java | 12 +-
sshd-core/pom.xml | 19 +-
.../org/apache/sshd/sshd-version.properties | 23 -
.../java/org/apache/sshd/client/SshClient.java | 60 +
.../auth/AuthenticationIdentitiesProvider.java | 125 --
.../auth/hostbased/HostKeyIdentityProvider.java | 53 -
.../keyboard/UserAuthKeyboardInteractive.java | 3 +-
.../auth/password/PasswordIdentityProvider.java | 183 ---
.../client/auth/password/UserAuthPassword.java | 2 +-
.../client/auth/pubkey/PublicKeyIdentity.java | 42 -
.../auth/pubkey/UserAuthPublicKeyIterator.java | 2 +-
.../config/SshClientConfigFileReader.java | 3 +-
.../hosts/ConfigFileHostEntryResolver.java | 107 --
.../DefaultConfigFileHostEntryResolver.java | 95 --
.../client/config/hosts/HostConfigEntry.java | 1169 ------------------
.../config/hosts/HostConfigEntryResolver.java | 60 -
.../client/config/hosts/HostPatternValue.java | 97 --
.../client/config/hosts/HostPatternsHolder.java | 343 -----
.../client/config/hosts/KnownHostDigest.java | 66 -
.../client/config/hosts/KnownHostEntry.java | 276 -----
.../client/config/hosts/KnownHostHashValue.java | 170 ---
.../keys/BuiltinClientIdentitiesWatcher.java | 106 --
.../config/keys/ClientIdentitiesWatcher.java | 139 ---
.../sshd/client/config/keys/ClientIdentity.java | 325 -----
.../config/keys/ClientIdentityFileWatcher.java | 141 ---
.../config/keys/ClientIdentityLoader.java | 95 --
.../config/keys/ClientIdentityProvider.java | 42 -
.../keys/DefaultClientIdentitiesWatcher.java | 66 -
.../KnownHostsServerKeyVerifier.java | 6 +-
.../sshd/client/session/ClientSession.java | 54 +
.../client/simple/SimpleClientConfigurator.java | 4 +-
.../org/apache/sshd/common/AttributeStore.java | 152 ---
.../org/apache/sshd/common/BuiltinFactory.java | 40 -
.../java/org/apache/sshd/common/Closeable.java | 126 --
.../java/org/apache/sshd/common/Factory.java | 41 -
.../org/apache/sshd/common/FactoryManager.java | 16 +
.../org/apache/sshd/common/NamedFactory.java | 66 -
.../org/apache/sshd/common/NamedResource.java | 104 --
.../org/apache/sshd/common/OptionalFeature.java | 92 --
.../apache/sshd/common/PropertyResolver.java | 124 --
.../sshd/common/PropertyResolverUtils.java | 482 --------
.../apache/sshd/common/RuntimeSshException.java | 48 -
.../org/apache/sshd/common/SshConstants.java | 245 ----
.../org/apache/sshd/common/SshException.java | 72 --
.../apache/sshd/common/SyspropsMapWrapper.java | 209 ----
.../sshd/common/auth/MutableUserHolder.java | 27 -
.../apache/sshd/common/auth/UsernameHolder.java | 32 -
.../sshd/common/channel/AbstractChannel.java | 6 -
.../org/apache/sshd/common/channel/Channel.java | 26 +
.../apache/sshd/common/cipher/BaseCipher.java | 113 --
.../sshd/common/cipher/BaseRC4Cipher.java | 54 -
.../sshd/common/cipher/BuiltinCiphers.java | 348 ------
.../org/apache/sshd/common/cipher/Cipher.java | 89 --
.../sshd/common/cipher/CipherFactory.java | 31 -
.../sshd/common/cipher/CipherInformation.java | 45 -
.../apache/sshd/common/cipher/CipherNone.java | 63 -
.../org/apache/sshd/common/cipher/ECCurves.java | 580 ---------
.../org/apache/sshd/common/cipher/package.html | 26 -
.../common/compression/BaseCompression.java | 48 -
.../common/compression/BuiltinCompressions.java | 239 ----
.../sshd/common/compression/Compression.java | 71 --
.../compression/CompressionDelayedZlib.java | 40 -
.../common/compression/CompressionFactory.java | 31 -
.../compression/CompressionInformation.java | 42 -
.../common/compression/CompressionNone.java | 76 --
.../common/compression/CompressionZlib.java | 85 --
.../apache/sshd/common/compression/package.html | 25 -
.../common/config/CompressionConfigValue.java | 94 --
.../common/config/FactoriesListParseResult.java | 51 -
.../sshd/common/config/ListParseResult.java | 66 -
.../sshd/common/config/LogLevelValue.java | 56 -
.../config/NamedFactoriesListParseResult.java | 47 -
.../config/NamedResourceListParseResult.java | 57 -
.../sshd/common/config/SshConfigFileReader.java | 268 +---
.../sshd/common/config/SyslogFacilityValue.java | 51 -
.../sshd/common/config/TimeValueConfig.java | 180 ---
.../sshd/common/config/VersionProperties.java | 98 --
.../common/config/keys/AuthorizedKeyEntry.java | 492 --------
.../common/config/keys/BuiltinIdentities.java | 212 ----
.../config/keys/FilePasswordProvider.java | 45 -
.../sshd/common/config/keys/Identity.java | 42 -
.../config/keys/IdentityResourceLoader.java | 49 -
.../sshd/common/config/keys/IdentityUtils.java | 159 ---
.../common/config/keys/KeyEntryResolver.java | 190 ---
.../sshd/common/config/keys/KeyRandomArt.java | 310 -----
.../sshd/common/config/keys/KeyUtils.java | 937 --------------
.../config/keys/PrivateKeyEntryDecoder.java | 142 ---
.../config/keys/PrivateKeyEntryResolver.java | 70 --
.../sshd/common/config/keys/PublicKeyEntry.java | 286 -----
.../config/keys/PublicKeyEntryDecoder.java | 114 --
.../config/keys/PublicKeyEntryResolver.java | 70 --
.../impl/AbstractIdentityResourceLoader.java | 62 -
.../keys/impl/AbstractKeyEntryResolver.java | 59 -
.../impl/AbstractPrivateKeyEntryDecoder.java | 40 -
.../impl/AbstractPublicKeyEntryDecoder.java | 41 -
.../keys/impl/DSSPublicKeyEntryDecoder.java | 119 --
.../keys/impl/ECDSAPublicKeyEntryDecoder.java | 178 ---
.../config/keys/impl/RSAPublicKeyDecoder.java | 117 --
.../keys/loader/AESPrivateKeyObfuscator.java | 110 --
.../loader/AbstractKeyPairResourceParser.java | 181 ---
.../loader/AbstractPrivateKeyObfuscator.java | 191 ---
.../keys/loader/DESPrivateKeyObfuscator.java | 76 --
.../keys/loader/KeyPairResourceLoader.java | 129 --
.../keys/loader/KeyPairResourceParser.java | 192 ---
.../loader/PrivateKeyEncryptionContext.java | 270 ----
.../keys/loader/PrivateKeyObfuscator.java | 64 -
.../OpenSSHDSSPrivateKeyEntryDecoder.java | 139 ---
.../OpenSSHECDSAPrivateKeyEntryDecoder.java | 164 ---
.../openssh/OpenSSHKeyPairResourceParser.java | 358 ------
.../loader/openssh/OpenSSHParserContext.java | 83 --
.../openssh/OpenSSHRSAPrivateKeyDecoder.java | 135 --
.../pem/AbstractPEMResourceKeyPairParser.java | 167 ---
.../loader/pem/DSSPEMResourceKeyPairParser.java | 126 --
.../pem/ECDSAPEMResourceKeyPairParser.java | 220 ----
.../loader/pem/KeyPairPEMResourceParser.java | 37 -
.../keys/loader/pem/PEMResourceParserUtils.java | 109 --
.../pem/PKCS8PEMResourceKeyPairParser.java | 156 ---
.../loader/pem/RSAPEMResourceKeyPairParser.java | 142 ---
.../apache/sshd/common/digest/BaseDigest.java | 156 ---
.../sshd/common/digest/BuiltinDigests.java | 166 ---
.../org/apache/sshd/common/digest/Digest.java | 36 -
.../sshd/common/digest/DigestFactory.java | 32 -
.../sshd/common/digest/DigestInformation.java | 36 -
.../apache/sshd/common/digest/DigestUtils.java | 228 ----
.../org/apache/sshd/common/digest/package.html | 25 -
.../sshd/common/future/AbstractSshFuture.java | 194 ---
.../apache/sshd/common/future/CloseFuture.java | 40 -
.../sshd/common/future/DefaultCloseFuture.java | 52 -
.../sshd/common/future/DefaultSshFuture.java | 236 ----
.../future/DefaultVerifiableSshFuture.java | 30 -
.../apache/sshd/common/future/SshFuture.java | 48 -
.../sshd/common/future/SshFutureListener.java | 42 -
.../sshd/common/future/VerifiableFuture.java | 68 -
.../sshd/common/future/WaitableFuture.java | 118 --
.../common/helpers/AbstractFactoryManager.java | 6 -
.../keyprovider/AbstractKeyPairProvider.java | 32 -
.../AbstractResourceKeyPairProvider.java | 234 ----
.../ClassLoadableResourceKeyPairProvider.java | 113 --
.../common/keyprovider/FileKeyPairProvider.java | 92 --
.../common/keyprovider/KeyIdentityProvider.java | 204 ---
.../common/keyprovider/KeyPairProvider.java | 181 ---
.../keyprovider/KeyPairProviderHolder.java | 35 -
.../keyprovider/MappedKeyPairProvider.java | 97 --
.../org/apache/sshd/common/mac/BaseMac.java | 111 --
.../org/apache/sshd/common/mac/BuiltinMacs.java | 273 ----
.../java/org/apache/sshd/common/mac/Mac.java | 52 -
.../org/apache/sshd/common/mac/MacFactory.java | 31 -
.../apache/sshd/common/mac/MacInformation.java | 41 -
.../org/apache/sshd/common/mac/package.html | 25 -
.../sshd/common/random/AbstractRandom.java | 34 -
.../common/random/AbstractRandomFactory.java | 43 -
.../apache/sshd/common/random/JceRandom.java | 60 -
.../sshd/common/random/JceRandomFactory.java | 42 -
.../org/apache/sshd/common/random/Random.java | 56 -
.../sshd/common/random/RandomFactory.java | 31 -
.../common/random/SingletonRandomFactory.java | 70 --
.../org/apache/sshd/common/random/package.html | 25 -
.../org/apache/sshd/common/session/Session.java | 27 +
.../common/session/helpers/AbstractSession.java | 6 -
.../common/signature/AbstractSignature.java | 149 ---
.../common/signature/BuiltinSignatures.java | 305 -----
.../apache/sshd/common/signature/Signature.java | 88 --
.../sshd/common/signature/SignatureDSA.java | 142 ---
.../sshd/common/signature/SignatureECDSA.java | 144 ---
.../signature/SignatureFactoriesManager.java | 94 --
.../sshd/common/signature/SignatureFactory.java | 31 -
.../sshd/common/signature/SignatureRSA.java | 89 --
.../apache/sshd/common/signature/package.html | 25 -
.../sshd/common/util/EventListenerUtils.java | 212 ----
.../apache/sshd/common/util/EventNotifier.java | 35 -
.../apache/sshd/common/util/GenericUtils.java | 915 --------------
.../sshd/common/util/IgnoringEmptyMap.java | 128 --
.../sshd/common/util/Int2IntFunction.java | 66 -
.../org/apache/sshd/common/util/Invoker.java | 116 --
.../apache/sshd/common/util/MapEntryUtils.java | 51 -
.../apache/sshd/common/util/NumberUtils.java | 313 -----
.../apache/sshd/common/util/ObjectBuilder.java | 38 -
.../org/apache/sshd/common/util/OsUtils.java | 257 ----
.../org/apache/sshd/common/util/Readable.java | 53 -
.../sshd/common/util/ReflectionUtils.java | 53 -
.../apache/sshd/common/util/SelectorUtils.java | 805 ------------
.../sshd/common/util/SshdEventListener.java | 44 -
.../apache/sshd/common/util/ValidateUtils.java | 216 ----
.../apache/sshd/common/util/VersionInfo.java | 137 --
.../apache/sshd/common/util/buffer/Buffer.java | 798 ------------
.../common/util/buffer/BufferException.java | 30 -
.../sshd/common/util/buffer/BufferUtils.java | 604 ---------
.../common/util/buffer/ByteArrayBuffer.java | 253 ----
.../keys/AbstractBufferPublicKeyParser.java | 88 --
.../util/buffer/keys/BufferPublicKeyParser.java | 111 --
.../buffer/keys/DSSBufferPublicKeyParser.java | 52 -
.../buffer/keys/ECBufferPublicKeyParser.java | 81 --
.../keys/ED25519BufferPublicKeyParser.java | 47 -
.../buffer/keys/RSABufferPublicKeyParser.java | 49 -
.../util/closeable/AbstractCloseable.java | 162 ---
.../util/closeable/AbstractInnerCloseable.java | 48 -
.../sshd/common/util/closeable/Builder.java | 115 --
.../common/util/closeable/FuturesCloseable.java | 76 --
.../common/util/closeable/IoBaseCloseable.java | 35 -
.../util/closeable/ParallelCloseable.java | 73 --
.../util/closeable/SequentialCloseable.java | 71 --
.../common/util/closeable/SimpleCloseable.java | 71 --
.../util/io/CloseableEmptyInputStream.java | 96 --
.../sshd/common/util/io/DirectoryScanner.java | 380 ------
.../sshd/common/util/io/EmptyInputStream.java | 66 -
.../sshd/common/util/io/FileInfoExtractor.java | 53 -
.../common/util/io/InputStreamWithChannel.java | 32 -
.../org/apache/sshd/common/util/io/IoUtils.java | 556 ---------
.../sshd/common/util/io/LimitInputStream.java | 113 --
.../util/io/LoggingFilterOutputStream.java | 67 -
.../common/util/io/ModifiableFileWatcher.java | 258 ----
.../sshd/common/util/io/NoCloseInputStream.java | 47 -
.../common/util/io/NoCloseOutputStream.java | 47 -
.../sshd/common/util/io/NoCloseReader.java | 46 -
.../sshd/common/util/io/NoCloseWriter.java | 46 -
.../sshd/common/util/io/NullInputStream.java | 90 --
.../sshd/common/util/io/NullOutputStream.java | 72 --
.../common/util/io/OutputStreamWithChannel.java | 32 -
.../sshd/common/util/io/der/ASN1Class.java | 93 --
.../sshd/common/util/io/der/ASN1Object.java | 338 -----
.../sshd/common/util/io/der/ASN1Type.java | 118 --
.../sshd/common/util/io/der/DERParser.java | 151 ---
.../sshd/common/util/io/der/DERWriter.java | 172 ---
.../common/util/io/functors/IOFunction.java | 86 --
.../util/logging/AbstractLoggingBean.java | 72 --
.../sshd/common/util/logging/LoggingUtils.java | 549 --------
.../sshd/common/util/logging/SimplifiedLog.java | 55 -
.../sshd/common/util/net/NetworkConnector.java | 90 --
.../sshd/common/util/net/SshdSocketAddress.java | 639 ----------
.../AbstractSecurityProviderRegistrar.java | 129 --
.../util/security/SecurityEntityFactory.java | 194 ---
.../util/security/SecurityProviderChoice.java | 130 --
.../security/SecurityProviderRegistrar.java | 337 -----
.../common/util/security/SecurityUtils.java | 759 ------------
.../BouncyCastleGeneratorHostKeyProvider.java | 48 -
.../BouncyCastleKeyPairResourceParser.java | 130 --
.../bouncycastle/BouncyCastleRandom.java | 88 --
.../bouncycastle/BouncyCastleRandomFactory.java | 45 -
.../BouncyCastleSecurityProviderRegistrar.java | 128 --
.../security/eddsa/Ed25519PublicKeyDecoder.java | 97 --
.../eddsa/EdDSASecurityProviderRegistrar.java | 104 --
.../eddsa/EdDSASecurityProviderUtils.java | 201 ---
.../OpenSSHEd25519PrivateKeyEntryDecoder.java | 172 ---
.../util/security/eddsa/SignatureEd25519.java | 49 -
.../util/threads/CloseableExecutorService.java | 28 -
.../util/threads/ExecutorServiceCarrier.java | 31 -
.../common/util/threads/NoCloseExecutor.java | 160 ---
.../util/threads/SshThreadPoolExecutor.java | 138 ---
.../common/util/threads/SshdThreadFactory.java | 78 --
.../sshd/common/util/threads/ThreadUtils.java | 185 ---
.../auth/pubkey/PublickeyAuthenticator.java | 17 +
.../config/SshServerConfigFileReader.java | 22 +-
.../keys/AuthorizedKeysAuthenticator.java | 4 +-
.../AbstractGeneratorHostKeyProvider.java | 293 -----
.../SimpleGeneratorHostKeyProvider.java | 67 -
.../java/org/apache/sshd/KeepAliveTest.java | 6 +-
.../java/org/apache/sshd/agent/AgentTest.java | 4 +-
.../sshd/client/ClientSessionListenerTest.java | 6 +-
.../auth/PasswordIdentityProviderTest.java | 73 --
.../sshd/client/channel/ChannelExecTest.java | 6 +-
.../hosts/ConfigFileHostEntryResolverTest.java | 139 ---
.../hosts/HostConfigEntryResolverTest.java | 11 +-
.../config/hosts/HostConfigEntryTest.java | 325 -----
.../config/hosts/KnownHostHashValueTest.java | 79 --
.../BuiltinClientIdentitiesWatcherTest.java | 161 ---
.../keys/ClientIdentityFileWatcherTest.java | 121 --
.../client/config/keys/ClientIdentityTest.java | 100 --
.../org/apache/sshd/client/kex/KexTest.java | 6 +-
.../KnownHostsServerKeyVerifierTest.java | 6 +-
.../sshd/client/session/ClientSessionTest.java | 6 +-
.../client/simple/SimpleSessionClientTest.java | 6 +-
.../apache/sshd/common/AttributeStoreTest.java | 24 +-
.../sshd/common/PropertyResolverUtilsTest.java | 24 +-
.../apache/sshd/common/SshConstantsTest.java | 75 --
.../sshd/common/VersionPropertiesTest.java | 45 -
.../sshd/common/auth/AuthenticationTest.java | 18 +-
.../sshd/common/cipher/AES192CTRTest.java | 41 -
.../sshd/common/cipher/AES256CBCTest.java | 41 -
.../sshd/common/cipher/ARCFOUR128Test.java | 39 -
.../sshd/common/cipher/ARCFOUR256Test.java | 41 -
.../sshd/common/cipher/BaseCipherTest.java | 95 --
.../apache/sshd/common/cipher/CipherTest.java | 25 +-
.../apache/sshd/common/cipher/ECCurvesTest.java | 107 --
.../compression/BuiltinCompressionsTest.java | 171 ---
.../common/compression/CompressionTest.java | 7 +-
.../common/config/SshConfigFileReaderTest.java | 39 +-
.../sshd/common/config/TimeValueConfigTest.java | 57 -
...AuthorizedKeyEntryLoginOptionsParseTest.java | 124 --
.../config/keys/AuthorizedKeyEntryTest.java | 6 +-
.../config/keys/BuiltinIdentitiesTest.java | 116 --
.../common/config/keys/KeyRandomArtTest.java | 117 --
.../common/config/keys/KeyUtilsCloneTest.java | 120 --
.../KeyUtilsFingerprintCaseSensitivityTest.java | 96 --
.../keys/KeyUtilsFingerprintGenerationTest.java | 159 ---
.../sshd/common/config/keys/KeyUtilsTest.java | 157 ---
.../common/config/keys/PublicKeyEntryTest.java | 62 -
.../loader/AESPrivateKeyObfuscatorTest.java | 79 --
.../OpenSSHKeyPairResourceParserTest.java | 108 --
.../pem/PKCS8PEMResourceKeyPairParserTest.java | 107 --
.../sshd/common/digest/BuiltinDigestsTest.java | 65 -
.../file/root/RootedFileSystemProviderTest.java | 4 +-
.../common/forward/PortForwardingLoadTest.java | 6 +-
.../sshd/common/forward/PortForwardingTest.java | 14 +-
.../common/future/DefaultSshFutureTest.java | 137 --
.../common/keyprovider/KeyPairProviderTest.java | 79 --
.../apache/sshd/common/mac/BuiltinMacsTest.java | 166 ---
.../org/apache/sshd/common/mac/MacTest.java | 7 +-
.../apache/sshd/common/mac/MacVectorsTest.java | 311 -----
.../sshd/common/random/RandomFactoryTest.java | 83 --
.../session/helpers/AbstractSessionTest.java | 2 +-
.../common/signature/BuiltinSignaturesTest.java | 149 ---
.../sshd/common/signature/SignatureDSATest.java | 109 --
.../signature/SignatureFactoriesTest.java | 6 +-
.../sshd/common/signature/SignatureRSATest.java | 119 --
.../common/signature/SignaturesDevelopment.java | 79 --
.../common/util/EventListenerUtilsTest.java | 149 ---
.../sshd/common/util/GenericUtilsTest.java | 173 ---
.../sshd/common/util/Int2IntFunctionTest.java | 154 ---
.../sshd/common/util/NumberUtilsTest.java | 77 --
.../apache/sshd/common/util/OsUtilsTest.java | 135 --
.../sshd/common/util/SecurityUtilsTest.java | 232 ----
.../sshd/common/util/SelectorUtilsTest.java | 147 ---
.../sshd/common/util/ThreadUtilsTest.java | 72 --
.../sshd/common/util/ValidateUtilsTest.java | 44 -
.../sshd/common/util/VersionInfoTest.java | 52 -
.../sshd/common/util/buffer/BufferTest.java | 103 --
.../common/util/buffer/BufferUtilsTest.java | 73 --
.../util/closeable/CloseableUtilsTest.java | 163 ---
.../common/util/io/EmptyInputStreamTest.java | 120 --
.../apache/sshd/common/util/io/IoUtilsTest.java | 61 -
.../common/util/io/LimitInputStreamTest.java | 118 --
.../util/io/ModifiableFileWatcherTest.java | 82 --
.../common/util/io/NoCloseInputStreamTest.java | 89 --
.../common/util/io/NoCloseOutputStreamTest.java | 69 --
.../sshd/common/util/io/NoCloseReaderTest.java | 95 --
.../sshd/common/util/io/NoCloseWriterTest.java | 73 --
.../common/util/io/NullInputStreamTest.java | 118 --
.../common/util/io/NullOutputStreamTest.java | 80 --
.../sshd/common/util/io/der/ASN1ClassTest.java | 67 -
.../sshd/common/util/io/der/ASN1TypeTest.java | 67 -
.../sshd/common/util/io/der/DERParserTest.java | 61 -
.../sshd/common/util/io/der/DERWriterTest.java | 64 -
.../util/net/SshdSocketIpv6AddressTest.java | 88 --
...SecurityProviderRegistrarCipherNameTest.java | 75 --
.../SecurityProviderRegistrarTestSupport.java | 62 -
.../util/security/eddsa/EDDSAProviderTest.java | 126 --
.../util/security/eddsa/Ed25519VectorsTest.java | 238 ----
.../EdDSASecurityProviderRegistrarTest.java | 85 --
.../sshd/server/ServerSessionListenerTest.java | 6 +-
.../server/auth/WelcomeBannerPhaseTest.java | 6 +-
.../sshd/server/auth/WelcomeBannerTest.java | 6 +-
.../AbstractGeneratorHostKeyProviderTest.java | 83 --
.../PEMGeneratorHostKeyProviderTest.java | 141 ---
.../SimpleGeneratorHostKeyProviderTest.java | 133 --
.../apache/sshd/util/test/BaseTestSupport.java | 543 +-------
.../sshd/util/test/CoreTestSupportUtils.java | 63 +
.../test/JUnit4ClassRunnerWithParameters.java | 48 -
.../JUnit4ClassRunnerWithParametersFactory.java | 58 -
.../test/JUnit4SingleInstanceClassRunner.java | 54 -
.../org/apache/sshd/util/test/NoIoTestCase.java | 30 -
.../test/OutputCountTrackingOutputStream.java | 56 -
.../apache/sshd/util/test/TeeOutputStream.java | 64 -
.../java/org/apache/sshd/util/test/Utils.java | 652 ----------
.../testReadGlobalHostsConfigEntries.config.txt | 22 -
.../testReadMultipleHostPatterns.config.txt | 5 -
.../testReadSimpleHostsConfigEntries.config.txt | 8 -
.../org/apache/sshd/client/config/keys/id_dsa | 12 -
.../org/apache/sshd/client/config/keys/id_ecdsa | 5 -
.../apache/sshd/client/config/keys/id_rsa_key | 27 -
...OpenSSHKeyPairResourceParserTest-DSA-KeyPair | 21 -
...SSHKeyPairResourceParserTest-DSA-KeyPair.pub | 1 -
...enSSHKeyPairResourceParserTest-ECDSA-KeyPair | 12 -
...HKeyPairResourceParserTest-ECDSA-KeyPair.pub | 1 -
...SSHKeyPairResourceParserTest-ED25519-KeyPair | 7 -
...eyPairResourceParserTest-ED25519-KeyPair.pub | 1 -
...OpenSSHKeyPairResourceParserTest-RSA-KeyPair | 49 -
...SSHKeyPairResourceParserTest-RSA-KeyPair.pub | 1 -
.../EDDSAProviderTest-EDDSA-OpenSSH-KeyPair | 7 -
.../EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub | 1 -
.../common/util/SecurityUtilsTest-DSA-KeyPair | 12 -
.../util/SecurityUtilsTest-DSA-KeyPair.pub | 1 -
.../util/SecurityUtilsTest-EC-256-KeyPair | 5 -
.../util/SecurityUtilsTest-EC-256-KeyPair.pub | 1 -
.../util/SecurityUtilsTest-EC-384-KeyPair | 6 -
.../util/SecurityUtilsTest-EC-384-KeyPair.pub | 1 -
.../util/SecurityUtilsTest-EC-521-KeyPair | 7 -
.../util/SecurityUtilsTest-EC-521-KeyPair.pub | 1 -
.../common/util/SecurityUtilsTest-RSA-KeyPair | 27 -
.../util/SecurityUtilsTest-RSA-KeyPair.pub | 1 -
.../super-secret-passphrase-RSA-AES-128-key | 30 -
.../super-secret-passphrase-RSA-AES-128-key.pub | 1 -
.../super-secret-passphrase-RSA-AES-192-key | 30 -
.../super-secret-passphrase-RSA-AES-192-key.pub | 1 -
.../super-secret-passphrase-RSA-AES-256-key | 30 -
.../super-secret-passphrase-RSA-AES-256-key.pub | 1 -
.../super-secret-passphrase-RSA-DES-EDE3-key | 30 -
...super-secret-passphrase-RSA-DES-EDE3-key.pub | 1 -
sshd-git/pom.xml | 7 +
.../sshd/git/pack/GitPackCommandTest.java | 6 +-
.../apache/sshd/git/pgm/GitPgmCommandTest.java | 4 +-
sshd-ldap/pom.xml | 7 +
.../sshd/server/auth/BaseAuthenticatorTest.java | 17 +-
sshd-mina/pom.xml | 7 +
sshd-netty/pom.xml | 7 +
sshd-scp/pom.xml | 9 +-
.../org/apache/sshd/client/scp/ScpTest.java | 154 +--
.../sshd/client/scp/SimpleScpClientTest.java | 20 +-
sshd-sftp/pom.xml | 9 +-
.../subsystem/sftp/SftpFileSystemProvider.java | 8 +-
.../sftp/AbstractSftpClientTestSupport.java | 6 +-
.../sftp/DefaultCloseableHandleTest.java | 4 +-
.../subsystem/sftp/SftpFileSystemTest.java | 30 +-
.../sshd/client/subsystem/sftp/SftpTest.java | 78 +-
.../subsystem/sftp/SftpVersionSelectorTest.java | 4 +-
.../client/subsystem/sftp/SftpVersionsTest.java | 24 +-
.../subsystem/sftp/SimpleSftpClientTest.java | 8 +-
.../helpers/AbstractCheckFileExtensionTest.java | 8 +-
.../helpers/AbstractMD5HashExtensionTest.java | 8 +-
.../helpers/CopyDataExtensionImplTest.java | 8 +-
.../helpers/CopyFileExtensionImplTest.java | 11 +-
.../SpaceAvailableExtensionImplTest.java | 6 +-
.../openssh/helpers/OpenSSHExtensionsTest.java | 10 +-
.../subsystem/sftp/SftpConstantsTest.java | 4 +-
.../sftp/SftpUniversalOwnerAndGroupTest.java | 4 +-
.../sftp/SftpSubsystemFactoryTest.java | 4 +-
sshd-spring-sftp/pom.xml | 7 +
.../sftp/ApacheSshdSftpSessionFactory.java | 4 +-
.../sftp/ApacheSshdSftpSessionFactoryTest.java | 21 +-
793 files changed, 44627 insertions(+), 44210 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/assembly/pom.xml
----------------------------------------------------------------------
diff --git a/assembly/pom.xml b/assembly/pom.xml
index 6efb67a..93011a8 100644
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -38,6 +38,11 @@
<dependencies>
<dependency>
<groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
<version>${project.version}</version>
</dependency>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 34d245f..a684d78 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1135,6 +1135,7 @@
</reporting>
<modules>
+ <module>sshd-common</module>
<module>sshd-core</module>
<module>sshd-mina</module>
<module>sshd-netty</module>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-cli/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-cli/pom.xml b/sshd-cli/pom.xml
index 3fdfb7a..3babf6b 100644
--- a/sshd-cli/pom.xml
+++ b/sshd-cli/pom.xml
@@ -61,6 +61,13 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
<!-- For the I/O factories -->
<dependency>
<groupId>org.apache.sshd</groupId>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
index 27c6d96..025c209 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
@@ -24,8 +24,8 @@ import java.net.SocketAddress;
import java.util.Map;
import java.util.Objects;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
import org.apache.sshd.common.config.LogLevelValue;
-import org.apache.sshd.common.config.SshConfigFileReader;
import org.apache.sshd.common.helpers.AbstractFactoryManager;
import org.apache.sshd.common.io.BuiltinIoServiceFactoryFactories;
import org.apache.sshd.common.io.IoAcceptor;
@@ -105,14 +105,14 @@ public abstract class CliSupport {
manager.setIoServiceFactoryFactory(factory.create());
- String levelValue = (options == null) ? null : Objects.toString(options.get(SshConfigFileReader.LOG_LEVEL_CONFIG_PROP), null);
+ String levelValue = (options == null) ? null : Objects.toString(options.get(ConfigFileReaderSupport.LOG_LEVEL_CONFIG_PROP), null);
if (GenericUtils.isEmpty(levelValue)) {
return manager;
}
LogLevelValue level = LogLevelValue.fromName(levelValue);
if (level == null) {
- throw new IllegalArgumentException("Unknown " + SshConfigFileReader.LOG_LEVEL_CONFIG_PROP + " option value: " + levelValue);
+ throw new IllegalArgumentException("Unknown " + ConfigFileReaderSupport.LOG_LEVEL_CONFIG_PROP + " option value: " + levelValue);
}
if ((level != LogLevelValue.FATAL) && (level != LogLevelValue.ERROR) && (level != LogLevelValue.INFO)) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
index 0d7d2f5..2d129f6 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
@@ -65,7 +65,7 @@ import org.apache.sshd.common.cipher.Cipher;
import org.apache.sshd.common.compression.BuiltinCompressions;
import org.apache.sshd.common.compression.Compression;
import org.apache.sshd.common.config.CompressionConfigValue;
-import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
import org.apache.sshd.common.config.keys.BuiltinIdentities;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.config.keys.PublicKeyEntry;
@@ -236,7 +236,7 @@ public abstract class SshClientCliSupport extends CliSupport {
}
if (port <= 0) {
- port = SshConfigFileReader.DEFAULT_PORT;
+ port = ConfigFileReaderSupport.DEFAULT_PORT;
}
// TODO use a configurable wait time
@@ -424,7 +424,7 @@ public abstract class SshClientCliSupport extends CliSupport {
}
String strictValue = Objects.toString(options.remove(KnownHostsServerKeyVerifier.STRICT_CHECKING_OPTION), "true");
- if (!SshConfigFileReader.parseBooleanValue(strictValue)) {
+ if (!ConfigFileReaderSupport.parseBooleanValue(strictValue)) {
return current;
}
@@ -506,7 +506,7 @@ public abstract class SshClientCliSupport extends CliSupport {
}
public static List<NamedFactory<Compression>> setupCompressions(PropertyResolver options, PrintStream stderr) {
- String argVal = PropertyResolverUtils.getString(options, SshConfigFileReader.COMPRESSION_PROP);
+ String argVal = PropertyResolverUtils.getString(options, ConfigFileReaderSupport.COMPRESSION_PROP);
if (GenericUtils.isEmpty(argVal)) {
return Collections.emptyList();
}
@@ -543,10 +543,10 @@ public abstract class SshClientCliSupport extends CliSupport {
}
public static List<NamedFactory<Mac>> setupMacs(PropertyResolver options, PrintStream stderr) {
- String argVal = PropertyResolverUtils.getString(options, SshConfigFileReader.MACS_CONFIG_PROP);
+ String argVal = PropertyResolverUtils.getString(options, ConfigFileReaderSupport.MACS_CONFIG_PROP);
return GenericUtils.isEmpty(argVal)
? Collections.emptyList()
- : setupMacs(SshConfigFileReader.MACS_CONFIG_PROP, argVal, null, stderr);
+ : setupMacs(ConfigFileReaderSupport.MACS_CONFIG_PROP, argVal, null, stderr);
}
public static List<NamedFactory<Mac>> setupMacs(String argName, String argVal, List<NamedFactory<Mac>> current, PrintStream stderr) {
@@ -571,10 +571,10 @@ public abstract class SshClientCliSupport extends CliSupport {
}
public static List<NamedFactory<Cipher>> setupCiphers(PropertyResolver options, PrintStream stderr) {
- String argVal = PropertyResolverUtils.getString(options, SshConfigFileReader.CIPHERS_CONFIG_PROP);
+ String argVal = PropertyResolverUtils.getString(options, ConfigFileReaderSupport.CIPHERS_CONFIG_PROP);
return GenericUtils.isEmpty(argVal)
? Collections.emptyList()
- : setupCiphers(SshConfigFileReader.CIPHERS_CONFIG_PROP, argVal, null, stderr);
+ : setupCiphers(ConfigFileReaderSupport.CIPHERS_CONFIG_PROP, argVal, null, stderr);
}
// returns null - e.g., re-specified or no supported cipher found
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
index b5350f0..adc120f 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
@@ -63,7 +63,7 @@ import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
import org.apache.sshd.common.config.keys.BuiltinIdentities;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.config.keys.PublicKeyEntry;
@@ -711,7 +711,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
public static <S extends SshKeyScanMain> S initializeScanner(S scanner, Collection<String> hosts) throws IOException {
setInputStream(scanner, hosts);
if (scanner.getPort() <= 0) {
- scanner.setPort(SshConfigFileReader.DEFAULT_PORT);
+ scanner.setPort(ConfigFileReaderSupport.DEFAULT_PORT);
}
if (scanner.getTimeout() <= 0L) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
index 8b392bc..6cdffc8 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
@@ -40,7 +40,7 @@ import org.apache.sshd.cli.CliSupport;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.PropertyResolver;
import org.apache.sshd.common.PropertyResolverUtils;
-import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
import org.apache.sshd.common.config.keys.BuiltinIdentities;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.keyprovider.KeyPairProvider;
@@ -166,7 +166,7 @@ public abstract class SshServerCliSupport extends CliSupport {
return subsystems;
}
- String nameList = (options == null) ? null : options.getString(SshConfigFileReader.SUBSYSTEM_CONFIG_PROP);
+ String nameList = (options == null) ? null : options.getString(ConfigFileReaderSupport.SUBSYSTEM_CONFIG_PROP);
if ("none".equalsIgnoreCase(nameList)) {
return Collections.emptyList();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java
index 0740d65..92bd182 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java
@@ -30,6 +30,7 @@ import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.PropertyResolver;
import org.apache.sshd.common.PropertyResolverUtils;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
import org.apache.sshd.common.config.SshConfigFileReader;
import org.apache.sshd.common.keyprovider.KeyPairProvider;
import org.apache.sshd.common.util.GenericUtils;
@@ -138,7 +139,7 @@ public class SshServerMain extends SshServerCliSupport {
keyFiles = new LinkedList<>();
}
keyFiles.add(optValue);
- } else if (SshConfigFileReader.PORT_CONFIG_PROP.equals(optName)) {
+ } else if (ConfigFileReaderSupport.PORT_CONFIG_PROP.equals(optName)) {
port = Integer.parseInt(optValue);
} else {
options.put(optName, optValue);
@@ -166,7 +167,7 @@ public class SshServerMain extends SshServerCliSupport {
setupServerBanner(sshd, resolver);
sshd.setPort(port);
- String macsOverride = resolver.getString(SshConfigFileReader.MACS_CONFIG_PROP);
+ String macsOverride = resolver.getString(ConfigFileReaderSupport.MACS_CONFIG_PROP);
if (GenericUtils.isNotEmpty(macsOverride)) {
SshConfigFileReader.configureMacs(sshd, macsOverride, true, true);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java b/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
index 51596b1..37cf8bc 100644
--- a/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
+++ b/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
@@ -35,7 +35,7 @@ import java.util.concurrent.Future;
import org.apache.sshd.common.PropertyResolver;
import org.apache.sshd.common.PropertyResolverUtils;
-import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
import org.apache.sshd.common.io.BuiltinIoServiceFactoryFactories;
import org.apache.sshd.common.io.IoServiceFactory;
import org.apache.sshd.common.util.GenericUtils;
@@ -56,7 +56,8 @@ import org.apache.sshd.server.scp.ScpCommandFactory;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.shell.ShellFactory;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
/**
* A basic implementation to allow remote mounting of the local file system via SFTP
@@ -249,7 +250,7 @@ public final class SshFsMounter extends SshServerCliSupport {
//////////////////////////////////////////////////////////////////////////
public static void main(String[] args) throws Exception {
- int port = SshConfigFileReader.DEFAULT_PORT;
+ int port = ConfigFileReaderSupport.DEFAULT_PORT;
boolean error = false;
Map<String, Object> options = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
int numArgs = GenericUtils.length(args);
@@ -296,7 +297,7 @@ public final class SshFsMounter extends SshServerCliSupport {
}
SshServer sshd = error ? null : setupIoServiceFactory(
- Utils.setupTestServer(SshFsMounter.class), options, System.out, System.err, args);
+ CoreTestSupportUtils.setupTestServer(SshFsMounter.class), options, System.out, System.err, args);
if (sshd == null) {
error = true;
}
@@ -309,7 +310,7 @@ public final class SshFsMounter extends SshServerCliSupport {
Map<String, Object> props = sshd.getProperties();
props.putAll(options);
PropertyResolver resolver = PropertyResolverUtils.toPropertyResolver(options);
- File targetFolder = Objects.requireNonNull(Utils.detectTargetFolder(MounterCommandFactory.class), "Failed to detect target folder");
+ File targetFolder = Objects.requireNonNull(CommonTestSupportUtils.detectTargetFolder(MounterCommandFactory.class), "Failed to detect target folder");
if (SecurityUtils.isBouncyCastleRegistered()) {
sshd.setKeyPairProvider(SecurityUtils.createGeneratorHostKeyProvider(new File(targetFolder, "key.pem").toPath()));
} else {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-common/pom.xml b/sshd-common/pom.xml
new file mode 100644
index 0000000..f893b19
--- /dev/null
+++ b/sshd-common/pom.xml
@@ -0,0 +1,128 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd</artifactId>
+ <version>2.0.1-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+
+ <artifactId>sshd-common</artifactId>
+ <name>Apache Mina SSHD :: Common support utilities</name>
+ <packaging>jar</packaging>
+ <inceptionYear>2018</inceptionYear>
+
+ <properties>
+ <projectRoot>${project.basedir}/..</projectRoot>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpg-jdk15on</artifactId>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ <optional>true</optional>
+ </dependency>
+
+ <!-- For ed25519 support -->
+ <dependency>
+ <groupId>net.i2p.crypto</groupId>
+ <artifactId>eddsa</artifactId>
+ <optional>true</optional>
+ </dependency>
+
+ <!-- test dependencies -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.not-yet-commons-ssl</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>src/main/filtered-resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <plugins>
+ <!-- publish the test-jar since it contains some classes used by other
+ artifacts tests -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ <configuration>
+ <includes>
+ <include>org/apache/sshd/util/test/**/*</include>
+ </includes>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <redirectTestOutputToFile>true</redirectTestOutputToFile>
+ <reportsDirectory>${project.build.directory}/surefire-reports-common</reportsDirectory>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/filtered-resources/org/apache/sshd/sshd-version.properties
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/filtered-resources/org/apache/sshd/sshd-version.properties b/sshd-common/src/main/filtered-resources/org/apache/sshd/sshd-version.properties
new file mode 100644
index 0000000..2c32c28
--- /dev/null
+++ b/sshd-common/src/main/filtered-resources/org/apache/sshd/sshd-version.properties
@@ -0,0 +1,23 @@
+##
+## 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.
+##
+
+groupId=${pom.groupId}
+artifactId=${pom.artifactId}
+version=${pom.version}
+sshd-version=${pom.artifactId}-${pom.version}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java b/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
new file mode 100644
index 0000000..cfd1f85
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
@@ -0,0 +1,125 @@
+/*
+ * 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.client.auth;
+
+import java.security.KeyPair;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface AuthenticationIdentitiesProvider extends KeyIdentityProvider, PasswordIdentityProvider {
+
+ /**
+ * Compares 2 password identities - returns zero ONLY if <U>both</U> compared
+ * objects are {@link String}s and equal to each other
+ */
+ Comparator<Object> PASSWORD_IDENTITY_COMPARATOR = (o1, o2) -> {
+ if (!(o1 instanceof String) || !(o2 instanceof String)) {
+ return -1;
+ } else {
+ return ((String) o1).compareTo((String) o2);
+ }
+ };
+
+ /**
+ * Compares 2 {@link KeyPair} identities - returns zero ONLY if <U>both</U> compared
+ * objects are {@link KeyPair}s and equal to each other
+ */
+ Comparator<Object> KEYPAIR_IDENTITY_COMPARATOR = (o1, o2) -> {
+ if ((!(o1 instanceof KeyPair)) || (!(o2 instanceof KeyPair))) {
+ return -1;
+ } else if (KeyUtils.compareKeyPairs((KeyPair) o1, (KeyPair) o2)) {
+ return 0;
+ } else {
+ return 1;
+ }
+ };
+
+ /**
+ * @return All the currently available identities - passwords, keys, etc...
+ */
+ Iterable<?> loadIdentities();
+
+ static int findIdentityIndex(List<?> identities, Comparator<? super Object> comp, Object target) {
+ for (int index = 0; index < identities.size(); index++) {
+ Object value = identities.get(index);
+ if (comp.compare(value, target) == 0) {
+ return index;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * @param identities The {@link Iterable} identities - OK if {@code null}/empty
+ * @return An {@link AuthenticationIdentitiesProvider} wrapping the identities
+ */
+ static AuthenticationIdentitiesProvider wrapIdentities(Iterable<?> identities) {
+ return new AuthenticationIdentitiesProvider() {
+ @Override
+ public Iterable<KeyPair> loadKeys() {
+ return selectIdentities(KeyPair.class);
+ }
+
+ @Override
+ public Iterable<String> loadPasswords() {
+ return selectIdentities(String.class);
+ }
+
+ @Override
+ public Iterable<?> loadIdentities() {
+ return selectIdentities(Object.class);
+ }
+
+ // NOTE: returns a NEW Collection on every call so that the original
+ // identities remain unchanged
+ private <T> Collection<T> selectIdentities(Class<T> type) {
+ Collection<T> matches = null;
+ for (Iterator<?> iter = GenericUtils.iteratorOf(identities); iter.hasNext();) {
+ Object o = iter.next();
+ Class<?> t = o.getClass();
+ if (!type.isAssignableFrom(t)) {
+ continue;
+ }
+
+ if (matches == null) {
+ matches = new LinkedList<>();
+ }
+
+ matches.add(type.cast(o));
+ }
+
+ return (matches == null) ? Collections.<T>emptyList() : matches;
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java b/sshd-common/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
new file mode 100644
index 0000000..81c26ca
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
@@ -0,0 +1,53 @@
+/*
+ * 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.client.auth.hostbased;
+
+import java.security.KeyPair;
+import java.security.cert.X509Certificate;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface HostKeyIdentityProvider {
+ /**
+ * @return The host keys as a {@link java.util.Map.Entry} of key + certificates (which can be {@code null}/empty)
+ */
+ Iterable<? extends Map.Entry<KeyPair, List<X509Certificate>>> loadHostKeys();
+
+ static Iterator<? extends Map.Entry<KeyPair, List<X509Certificate>>> iteratorOf(HostKeyIdentityProvider provider) {
+ return GenericUtils.iteratorOf((provider == null) ? null : provider.loadHostKeys());
+ }
+
+ static HostKeyIdentityProvider wrap(KeyPair... pairs) {
+ return wrap(GenericUtils.asList(pairs));
+ }
+
+ static HostKeyIdentityProvider wrap(Iterable<? extends KeyPair> pairs) {
+ return () -> GenericUtils.wrapIterable(pairs, kp -> new SimpleImmutableEntry<>(kp, Collections.<X509Certificate>emptyList()));
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java b/sshd-common/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java
new file mode 100644
index 0000000..f075bf8
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java
@@ -0,0 +1,169 @@
+/*
+ * 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.client.auth.password;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface PasswordIdentityProvider {
+
+ /**
+ * An "empty" implementation of {@link PasswordIdentityProvider} that returns
+ * and empty group of passwords
+ */
+ PasswordIdentityProvider EMPTY_PASSWORDS_PROVIDER = new PasswordIdentityProvider() {
+ @Override
+ public Iterable<String> loadPasswords() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String toString() {
+ return "EMPTY";
+ }
+ };
+
+ /**
+ * Invokes {@link PasswordIdentityProvider#loadPasswords()} and returns the result.
+ * Ignores {@code null} providers (i.e., returns an empty iterable instance)
+ */
+ Function<PasswordIdentityProvider, Iterable<String>> LOADER = p ->
+ (p == null) ? Collections.emptyList() : p.loadPasswords();
+
+ /**
+ * @return The currently available passwords - ignored if {@code null}
+ */
+ Iterable<String> loadPasswords();
+
+ /**
+ * Creates a "unified" {@link Iterator} of passwords out of 2 possible
+ * {@link PasswordIdentityProvider}
+ *
+ * @param identities The registered passwords
+ * @param passwords Extra available passwords
+ * @return The wrapping iterator
+ * @see #resolvePasswordIdentityProvider(PasswordIdentityProvider, PasswordIdentityProvider)
+ */
+ static Iterator<String> iteratorOf(PasswordIdentityProvider identities, PasswordIdentityProvider passwords) {
+ return iteratorOf(resolvePasswordIdentityProvider(identities, passwords));
+ }
+
+ /**
+ * Resolves a non-{@code null} iterator of the available passwords
+ *
+ * @param provider The {@link PasswordIdentityProvider} - ignored if {@code null} (i.e.,
+ * return an empty iterator)
+ * @return A non-{@code null} iterator - which may be empty if no provider or no passwords
+ */
+ static Iterator<String> iteratorOf(PasswordIdentityProvider provider) {
+ return GenericUtils.iteratorOf((provider == null) ? null : provider.loadPasswords());
+ }
+
+ /**
+ * <P>Creates a "unified" {@link PasswordIdentityProvider} out of 2 possible ones
+ * as follows:</P></BR>
+ * <UL>
+ * <LI>If both are {@code null} then return {@code null}.</LI>
+ * <LI>If either one is {@code null} then use the non-{@code null} one.</LI>
+ * <LI>If both are the same instance then use it.</U>
+ * <LI>Otherwise, returns a wrapper that groups both providers.</LI>
+ * </UL>
+ * @param identities The registered passwords
+ * @param passwords The extra available passwords
+ * @return The resolved provider
+ * @see #multiProvider(PasswordIdentityProvider...)
+ */
+ static PasswordIdentityProvider resolvePasswordIdentityProvider(
+ PasswordIdentityProvider identities, PasswordIdentityProvider passwords) {
+ if ((passwords == null) || (identities == passwords)) {
+ return identities;
+ } else if (identities == null) {
+ return passwords;
+ } else {
+ return multiProvider(identities, passwords);
+ }
+ }
+
+ /**
+ * Wraps a group of {@link PasswordIdentityProvider} into a single one
+ *
+ * @param providers The providers - ignored if {@code null}/empty (i.e., returns
+ * {@link #EMPTY_PASSWORDS_PROVIDER}
+ * @return The wrapping provider
+ * @see #multiProvider(Collection)
+ */
+ static PasswordIdentityProvider multiProvider(PasswordIdentityProvider... providers) {
+ return multiProvider(GenericUtils.asList(providers));
+ }
+
+ /**
+ * Wraps a group of {@link PasswordIdentityProvider} into a single one
+ *
+ * @param providers The providers - ignored if {@code null}/empty (i.e., returns
+ * {@link #EMPTY_PASSWORDS_PROVIDER}
+ * @return The wrapping provider
+ */
+ static PasswordIdentityProvider multiProvider(Collection<? extends PasswordIdentityProvider> providers) {
+ return GenericUtils.isEmpty(providers) ? EMPTY_PASSWORDS_PROVIDER : wrapPasswords(iterableOf(providers));
+ }
+
+ /**
+ * Wraps a group of {@link PasswordIdentityProvider} into an {@link Iterable} of their combined passwords
+ *
+ * @param providers The providers - ignored if {@code null}/empty (i.e., returns an empty iterable instance)
+ * @return The wrapping iterable
+ */
+ static Iterable<String> iterableOf(Collection<? extends PasswordIdentityProvider> providers) {
+ Iterable<Supplier<Iterable<String>>> passwordSuppliers =
+ GenericUtils.<PasswordIdentityProvider, Supplier<Iterable<String>>>wrapIterable(providers, p -> p::loadPasswords);
+ return GenericUtils.multiIterableSuppliers(passwordSuppliers);
+ }
+
+ /**
+ * Wraps a group of passwords into a {@link PasswordIdentityProvider}
+ *
+ * @param passwords The passwords - ignored if {@code null}/empty
+ * (i.e., returns {@link #EMPTY_PASSWORDS_PROVIDER})
+ * @return The provider wrapper
+ */
+ static PasswordIdentityProvider wrapPasswords(String... passwords) {
+ return wrapPasswords(GenericUtils.asList(passwords));
+ }
+
+ /**
+ * Wraps a group of passwords into a {@link PasswordIdentityProvider}
+ *
+ * @param passwords The passwords {@link Iterable} - ignored if {@code null}
+ * (i.e., returns {@link #EMPTY_PASSWORDS_PROVIDER})
+ * @return The provider wrapper
+ */
+ static PasswordIdentityProvider wrapPasswords(Iterable<String> passwords) {
+ return (passwords == null) ? EMPTY_PASSWORDS_PROVIDER : () -> passwords;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java b/sshd-common/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java
new file mode 100644
index 0000000..51444ae
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java
@@ -0,0 +1,42 @@
+/*
+ * 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.client.auth.pubkey;
+
+import java.security.PublicKey;
+
+/**
+ * Represents a public key identity
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface PublicKeyIdentity {
+ /**
+ * @return The {@link PublicKey} identity value
+ */
+ PublicKey getPublicKey();
+
+ /**
+ * Proves the public key identity by signing the given data
+ *
+ * @param data Data to sign
+ * @return Signed data - using the identity
+ * @throws Exception If failed to sign the data
+ */
+ byte[] sign(byte[] data) throws Exception;
+}
\ No newline at end of file
[13/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Object.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Object.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Object.java
deleted file mode 100644
index 1d32a40..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Object.java
+++ /dev/null
@@ -1,338 +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.util.io.der;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.Serializable;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ASN1Object implements Serializable, Cloneable {
- // Constructed Flag
- public static final byte CONSTRUCTED = 0x20;
-
- private static final long serialVersionUID = 4687581744706127265L;
-
- private ASN1Class objClass;
- private ASN1Type objType;
- private boolean constructed;
- private int length;
- private byte[] value;
-
- public ASN1Object() {
- super();
- }
-
- /*
- * <P>The first byte in DER encoding is made of following fields</P>
- * <pre>
- *-------------------------------------------------
- *|Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
- *-------------------------------------------------
- *| Class | CF | Type |
- *-------------------------------------------------
- * </pre>
- */
- public ASN1Object(byte tag, int len, byte... data) {
- this(ASN1Class.fromDERValue(tag), ASN1Type.fromDERValue(tag), (tag & CONSTRUCTED) == CONSTRUCTED, len, data);
- }
-
- public ASN1Object(ASN1Class c, ASN1Type t, boolean ctored, int len, byte... data) {
- objClass = c;
- objType = t;
- constructed = ctored;
- length = len;
- value = data;
- }
-
- public ASN1Class getObjClass() {
- return objClass;
- }
-
- public void setObjClass(ASN1Class c) {
- objClass = c;
- }
-
- public ASN1Type getObjType() {
- return objType;
- }
-
- public void setObjType(ASN1Type y) {
- objType = y;
- }
-
- public boolean isConstructed() {
- return constructed;
- }
-
- public void setConstructed(boolean c) {
- constructed = c;
- }
-
- public int getLength() {
- return length;
- }
-
- public void setLength(int l) {
- length = l;
- }
-
- public byte[] getValue() {
- return value;
- }
-
- // if length is less than value.length then returns copy of it
- public byte[] getPureValueBytes() {
- byte[] bytes = getValue();
- int available = getLength();
- int numBytes = NumberUtils.length(bytes);
- if (numBytes == available) {
- return bytes;
- }
-
- if (available == 0) {
- return GenericUtils.EMPTY_BYTE_ARRAY;
- }
-
- byte[] pure = new byte[available];
- System.arraycopy(bytes, 0, pure, 0, available);
- return pure;
- }
-
- public void setValue(byte[] v) {
- value = v;
- }
-
- public DERParser createParser() {
- return new DERParser(getValue(), 0, getLength());
- }
-
- public Object asObject() throws IOException {
- ASN1Type type = getObjType();
- if (type == null) {
- throw new IOException("No type set");
- }
-
- switch (type) {
- case INTEGER:
- return asInteger();
-
- case NUMERIC_STRING:
- case PRINTABLE_STRING:
- case VIDEOTEX_STRING:
- case IA5_STRING:
- case GRAPHIC_STRING:
- case ISO646_STRING:
- case GENERAL_STRING:
- case BMP_STRING:
- case UTF8_STRING:
- return asString();
-
- case OBJECT_IDENTIFIER:
- return asOID();
-
- case SEQUENCE :
- return getValue();
-
- default:
- throw new IOException("Invalid DER: unsupported type: " + type);
- }
- }
-
- /**
- * Get the value as {@link BigInteger}
- * @return BigInteger
- * @throws IOException if type not an {@link ASN1Type#INTEGER}
- */
- public BigInteger asInteger() throws IOException {
- ASN1Type typeValue = getObjType();
- if (ASN1Type.INTEGER.equals(typeValue)) {
- return toInteger();
- } else {
- throw new IOException("Invalid DER: object is not integer: " + typeValue);
- }
- }
-
- // does not check if this is an integer
- public BigInteger toInteger() {
- return new BigInteger(getPureValueBytes());
- }
-
- /**
- * Get value as string. Most strings are treated as Latin-1.
- * @return Java string
- * @throws IOException if
- */
- public String asString() throws IOException {
- ASN1Type type = getObjType();
- if (type == null) {
- throw new IOException("No type set");
- }
-
- final String encoding;
- switch (type) {
- // Not all are Latin-1 but it's the closest thing
- case NUMERIC_STRING:
- case PRINTABLE_STRING:
- case VIDEOTEX_STRING:
- case IA5_STRING:
- case GRAPHIC_STRING:
- case ISO646_STRING:
- case GENERAL_STRING:
- encoding = "ISO-8859-1";
- break;
-
- case BMP_STRING:
- encoding = "UTF-16BE";
- break;
-
- case UTF8_STRING:
- encoding = "UTF-8";
- break;
-
- case UNIVERSAL_STRING:
- throw new IOException("Invalid DER: can't handle UCS-4 string");
-
- default:
- throw new IOException("Invalid DER: object is not a string: " + type);
- }
-
- return new String(getValue(), 0, getLength(), encoding);
- }
-
- public List<Integer> asOID() throws IOException {
- ASN1Type typeValue = getObjType();
- if (ASN1Type.OBJECT_IDENTIFIER.equals(typeValue)) {
- return toOID();
- } else {
- throw new StreamCorruptedException("Invalid DER: object is not an OID: " + typeValue);
- }
- }
-
- // Does not check that type is OID
- public List<Integer> toOID() throws IOException {
- int vLen = getLength();
- if (vLen <= 0) {
- throw new EOFException("Not enough data for an OID");
- }
-
- List<Integer> oid = new ArrayList<>(vLen + 1);
- byte[] bytes = getValue();
- int val1 = bytes[0] & 0xFF;
- oid.add(Integer.valueOf(val1 / 40));
- oid.add(Integer.valueOf(val1 % 40));
-
- for (int curPos = 1; curPos < vLen; curPos++) {
- int v = bytes[curPos] & 0xFF;
- if (v <= 0x7F) { // short form
- oid.add(Integer.valueOf(v));
- continue;
- }
-
- long curVal = v & 0x7F;
- curPos++;
-
- for (int subLen = 1;; subLen++, curPos++) {
- if (curPos >= vLen) {
- throw new EOFException("Incomplete OID value");
- }
-
- if (subLen > 5) { // 32 bit values can span at most 5 octets
- throw new StreamCorruptedException("OID component encoding beyond 5 bytes");
- }
-
- v = bytes[curPos] & 0xFF;
- curVal = ((curVal << 7) & 0xFFFFFFFF80L) | (v & 0x7FL);
- if (curVal > Integer.MAX_VALUE) {
- throw new StreamCorruptedException("OID value exceeds 32 bits: " + curVal);
- }
-
- if (v <= 0x7F) { // found last octet ?
- break;
- }
- }
-
- oid.add(Integer.valueOf((int) (curVal & 0x7FFFFFFFL)));
- }
-
- return oid;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(getObjClass(), getObjType())
- + Boolean.hashCode(isConstructed())
- + getLength()
- + NumberUtils.hashCode(getValue(), 0, getLength());
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (this == obj) {
- return true;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
-
- ASN1Object other = (ASN1Object) obj;
- return Objects.equals(this.getObjClass(), other.getObjClass())
- && Objects.equals(this.getObjType(), other.getObjType())
- && (this.isConstructed() == other.isConstructed())
- && (this.getLength() == other.getLength())
- && (NumberUtils.diffOffset(this.getValue(), 0, other.getValue(), 0, this.getLength()) < 0);
- }
-
- @Override
- public ASN1Object clone() {
- try {
- ASN1Object cpy = getClass().cast(super.clone());
- byte[] data = cpy.getValue();
- if (data != null) {
- cpy.setValue(data.clone());
- }
- return cpy;
- } catch (CloneNotSupportedException e) {
- throw new IllegalStateException("Unexpected clone failure: " + e.getMessage(), e);
- }
- }
-
- @Override
- public String toString() {
- return Objects.toString(getObjClass())
- + "/" + getObjType()
- + "/" + isConstructed()
- + "[" + getLength() + "]"
- + ": " + BufferUtils.toHex(getValue(), 0, getLength(), ':');
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Type.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Type.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Type.java
deleted file mode 100644
index 3dd13ca..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Type.java
+++ /dev/null
@@ -1,118 +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.util.io.der;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Set;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum ASN1Type {
- ANY((byte) 0x00),
- BOOLEAN((byte) 0x01),
- INTEGER((byte) 0x02),
- BIT_STRING((byte) 0x03),
- OCTET_STRING((byte) 0x04),
- NULL((byte) 0x05),
- OBJECT_IDENTIFIER((byte) 0x06),
- REAL((byte) 0x09),
- ENUMERATED((byte) 0x0a),
- RELATIVE_OID((byte) 0x0d),
- SEQUENCE((byte) 0x10),
- SET((byte) 0x11),
- NUMERIC_STRING((byte) 0x12),
- PRINTABLE_STRING((byte) 0x13),
- T61_STRING((byte) 0x14),
- VIDEOTEX_STRING((byte) 0x15),
- IA5_STRING((byte) 0x16),
- GRAPHIC_STRING((byte) 0x19),
- ISO646_STRING((byte) 0x1A),
- GENERAL_STRING((byte) 0x1B),
- UTF8_STRING((byte) 0x0C),
- UNIVERSAL_STRING((byte) 0x1C),
- BMP_STRING((byte) 0x1E),
- UTC_TIME((byte) 0x17),
- GENERALIZED_TIME((byte) 0x18);
-
- public static final Set<ASN1Type> VALUES =
- Collections.unmodifiableSet(EnumSet.allOf(ASN1Type.class));
-
- private final byte typeValue;
-
- ASN1Type(byte typeVal) {
- typeValue = typeVal;
- }
-
- public byte getTypeValue() {
- return typeValue;
- }
-
- public static ASN1Type fromName(String s) {
- if (GenericUtils.isEmpty(s)) {
- return null;
- }
-
- for (ASN1Type t : VALUES) {
- if (s.equalsIgnoreCase(t.name())) {
- return t;
- }
- }
-
- return null;
- }
-
- /**
- * <P>The first byte in DER encoding is made of following fields</P>
- * <pre>
- *-------------------------------------------------
- *|Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
- *-------------------------------------------------
- *| Class | CF | Type |
- *-------------------------------------------------
- * </pre>
- * @param value The original DER encoded byte
- * @return The {@link ASN1Type} value - {@code null} if no match found
- * @see #fromTypeValue(int)
- */
- public static ASN1Type fromDERValue(int value) {
- return fromTypeValue(value & 0x1F);
- }
-
- /**
- * @param value The "pure" type value - with no extra bits set
- * @return The {@link ASN1Type} value - {@code null} if no match found
- */
- public static ASN1Type fromTypeValue(int value) {
- if ((value < 0) || (value > 0x1F)) { // only 5 bits are used
- return null;
- }
-
- for (ASN1Type t : VALUES) {
- if (t.getTypeValue() == value) {
- return t;
- }
- }
-
- return null;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
deleted file mode 100644
index 5f37bd7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
+++ /dev/null
@@ -1,151 +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.util.io.der;
-
-import java.io.ByteArrayInputStream;
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.util.Arrays;
-
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-
-/**
- * A bare minimum DER parser - just enough to be able to decode
- * signatures and private keys
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DERParser extends FilterInputStream {
- /**
- * Maximum size of data allowed by {@link #readLength()} - it is a bit
- * arbitrary since one can encode 32-bit length data, but it is good
- * enough for the keys
- */
- public static final int MAX_DER_VALUE_LENGTH = 2 * Short.MAX_VALUE;
-
- private final byte[] lenBytes = new byte[Integer.BYTES];
-
- public DERParser(byte... bytes) {
- this(bytes, 0, NumberUtils.length(bytes));
- }
-
- public DERParser(byte[] bytes, int offset, int len) {
- this(new ByteArrayInputStream(bytes, offset, len));
- }
-
- public DERParser(InputStream s) {
- super(s);
- }
-
- /**
- * Decode the length of the field. Can only support length
- * encoding up to 4 octets. In BER/DER encoding, length can
- * be encoded in 2 forms:
- * <ul>
- * <li><p>
- * Short form - One octet. Bit 8 has value "0" and bits 7-1
- * give the length.
- * </p></li>
- *
- * <li><p>
- * Long form - Two to 127 octets (only 4 is supported here).
- * Bit 8 of first octet has value "1" and bits 7-1 give the
- * number of additional length octets. Second and following
- * octets give the length, base 256, most significant digit
- * first.
- * </p></li>
- * </ul>
- *
- * @return The length as integer
- * @throws IOException If invalid format found
- */
- public int readLength() throws IOException {
- int i = read();
- if (i == -1) {
- throw new StreamCorruptedException("Invalid DER: length missing");
- }
-
- // A single byte short length
- if ((i & ~0x7F) == 0) {
- return i;
- }
-
- int num = i & 0x7F;
- // TODO We can't handle length longer than 4 bytes
- if ((i >= 0xFF) || (num > lenBytes.length)) {
- throw new StreamCorruptedException("Invalid DER: length field too big: " + i);
- }
-
- // place the read bytes last so that the 1st ones are zeroes as big endian
- Arrays.fill(lenBytes, (byte) 0);
- int n = read(lenBytes, 4 - num, num);
- if (n < num) {
- throw new StreamCorruptedException("Invalid DER: length data too short: expected=" + num + ", actual=" + n);
- }
-
- long len = BufferUtils.getUInt(lenBytes);
- if (len < 0x7FL) { // according to standard: "the shortest possible length encoding must be used"
- throw new StreamCorruptedException("Invalid DER: length not in shortest form: " + len);
- }
-
- if (len > MAX_DER_VALUE_LENGTH) {
- throw new StreamCorruptedException("Invalid DER: data length too big: " + len + " (max=" + MAX_DER_VALUE_LENGTH + ")");
- }
-
- // we know the cast is safe since it is less than MAX_DER_VALUE_LENGTH which is ~64K
- return (int) len;
- }
-
- public ASN1Object readObject() throws IOException {
- int tag = read();
- if (tag == -1) {
- return null;
- }
-
- int length = readLength();
- byte[] value = new byte[length];
- int n = read(value);
- if (n < length) {
- throw new StreamCorruptedException("Invalid DER: stream too short, missing value: read " + n + " out of required " + length);
- }
-
- return new ASN1Object((byte) tag, length, value);
- }
-
- public BigInteger readBigInteger() throws IOException {
- int type = read();
- if (type != 0x02) {
- throw new StreamCorruptedException("Invalid DER: data type is not an INTEGER: 0x" + Integer.toHexString(type));
- }
-
- int len = readLength();
- byte[] value = new byte[len];
- int n = read(value);
- if (n < len) {
- throw new StreamCorruptedException("Invalid DER: stream too short, missing value: read " + n + " out of required " + len);
- }
-
- return new BigInteger(value);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java
deleted file mode 100644
index bc603ee..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java
+++ /dev/null
@@ -1,172 +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.util.io.der;
-
-import java.io.ByteArrayOutputStream;
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-
-/**
- * A bare-minimum DER encoder - just enough so we can encoder signatures
- * and keys data
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DERWriter extends FilterOutputStream {
- private final byte[] lenBytes = new byte[Integer.BYTES];
-
- public DERWriter() {
- this(ByteArrayBuffer.DEFAULT_SIZE);
- }
-
- public DERWriter(int initialSize) {
- this(new ByteArrayOutputStream(initialSize));
- }
-
- public DERWriter(OutputStream stream) {
- super(Objects.requireNonNull(stream, "No output stream"));
- }
-
- public DERWriter startSequence() {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- AtomicBoolean dataWritten = new AtomicBoolean(false);
- @SuppressWarnings("resource")
- DERWriter encloser = this;
- return new DERWriter(baos) {
- @Override
- public void close() throws IOException {
- baos.close();
-
- if (!dataWritten.getAndSet(true)) { // detect repeated calls and write this only once
- encloser.writeObject(new ASN1Object(ASN1Class.UNIVERSAL, ASN1Type.SEQUENCE, false, baos.size(), baos.toByteArray()));
- }
- }
- };
- }
-
- public void writeBigInteger(BigInteger value) throws IOException {
- writeBigInteger(Objects.requireNonNull(value, "No value").toByteArray());
- }
-
- /**
- * The integer is always considered to be positive, so if the first byte is < 0,
- * we pad with a zero to make it positive
- *
- * @param bytes {@link BigInteger} bytes
- * @throws IOException If failed to write the bytes
- */
- public void writeBigInteger(byte... bytes) throws IOException {
- writeBigInteger(bytes, 0, NumberUtils.length(bytes));
- }
-
- /**
- * The integer is always considered to be positive, so if the first byte is < 0,
- * we pad with a zero to make it positive
- *
- * @param bytes {@link BigInteger} bytes
- * @param off Offset in bytes data
- * @param len Number of bytes to write
- * @throws IOException If failed to write the bytes
- */
- public void writeBigInteger(byte[] bytes, int off, int len) throws IOException {
- // Strip leading zeroes
- while (len > 1 && bytes[off] == 0 && isPositive(bytes[off + 1])) {
- off++;
- len--;
- }
- // indicate it is an INTEGER
- write(0x02);
- // Pad with a zero if needed
- if (isPositive(bytes[off])) {
- writeLength(len);
- } else {
- writeLength(len + 1);
- write(0);
- }
- // Write data
- write(bytes, off, len);
- }
-
- private boolean isPositive(byte b) {
- return (b & 0x80) == 0;
- }
-
- public void writeObject(ASN1Object obj) throws IOException {
- Objects.requireNonNull(obj, "No ASN.1 object");
-
- ASN1Type type = obj.getObjType();
- byte typeValue = type.getTypeValue();
- ASN1Class clazz = obj.getObjClass();
- byte classValue = clazz.getClassValue();
- byte tagValue = (byte) (((classValue << 6) & 0xC0) | (typeValue & 0x1F));
- writeObject(tagValue, obj.getLength(), obj.getValue());
- }
-
- public void writeObject(byte tag, int len, byte... data) throws IOException {
- write(tag & 0xFF);
- writeLength(len);
- write(data, 0, len);
- }
-
- public void writeLength(int len) throws IOException {
- ValidateUtils.checkTrue(len >= 0, "Invalid length: %d", len);
-
- // short form - MSBit is zero
- if (len <= 127) {
- write(len);
- return;
- }
-
- BufferUtils.putUInt(len, lenBytes);
-
- int nonZeroPos = 0;
- for (; nonZeroPos < lenBytes.length; nonZeroPos++) {
- if (lenBytes[nonZeroPos] != 0) {
- break;
- }
- }
-
- if (nonZeroPos >= lenBytes.length) {
- throw new StreamCorruptedException("All zeroes length representation for len=" + len);
- }
-
- int bytesLen = lenBytes.length - nonZeroPos;
- write(0x80 | bytesLen); // indicate number of octets
- write(lenBytes, nonZeroPos, bytesLen);
- }
-
- public byte[] toByteArray() throws IOException {
- if (this.out instanceof ByteArrayOutputStream) {
- return ((ByteArrayOutputStream) this.out).toByteArray();
- } else {
- throw new IOException("The underlying stream is not a byte[] stream");
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java
deleted file mode 100644
index a55ac2e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java
+++ /dev/null
@@ -1,86 +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.util.io.functors;
-
-import java.io.IOException;
-import java.util.Objects;
-
-/**
- * Invokes some I/O function on the input returning some output
- * and potentially throwing an {@link IOException} in the process
- *
- * @param <T> Type of input
- * @param <R> Type of output
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface IOFunction<T, R> {
- R apply(T t) throws IOException;
-
- /**
- * Returns a composed function that first applies the {@code before}
- * function to its input, and then applies this function to the result.
- * If evaluation of either function throws an exception, it is relayed to
- * the caller of the composed function.
- *
- * @param <V> the type of input to the {@code before} function, and to the
- * composed function
- * @param before the function to apply before this function is applied
- * @return a composed function that first applies the {@code before}
- * function and then applies this function
- * @throws NullPointerException if before is null
- *
- * @see #andThen(IOFunction)
- */
- default <V> IOFunction<V, R> compose(IOFunction<? super V, ? extends T> before) {
- Objects.requireNonNull(before, "No composing function provided");
- return (V v) -> apply(before.apply(v));
- }
-
- /**
- * Returns a composed function that first applies this function to
- * its input, and then applies the {@code after} function to the result.
- * If evaluation of either function throws an exception, it is relayed to
- * the caller of the composed function.
- *
- * @param <V> the type of output of the {@code after} function, and of the
- * composed function
- * @param after the function to apply after this function is applied
- * @return a composed function that first applies this function and then
- * applies the {@code after} function
- * @throws NullPointerException if after is null
- *
- * @see #compose(IOFunction)
- */
- default <V> IOFunction<T, V> andThen(IOFunction<? super R, ? extends V> after) {
- Objects.requireNonNull(after, "No composing function provided");
- return (T t) -> after.apply(apply(t));
- }
-
- /**
- * Returns a function that always returns its input argument.
- *
- * @param <T> the type of the input and output objects to the function
- * @return a function that always returns its input argument
- */
- static <T> IOFunction<T, T> identity() {
- return t -> t;
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java b/sshd-core/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
deleted file mode 100644
index 27fef2f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
+++ /dev/null
@@ -1,72 +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.util.logging;
-
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Serves as a common base class for the vast majority of classes that require
- * some kind of logging. Facilitates quick and easy replacement of the actual used
- * logger from one framework to another
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractLoggingBean {
- protected final Logger log;
- private final AtomicReference<SimplifiedLog> simplifiedLog = new AtomicReference<>();
-
- /**
- * Default constructor - creates a logger using the full class name
- */
- protected AbstractLoggingBean() {
- this("");
- }
-
- /**
- * Create a logger for instances of the same class for which we might
- * want to have a "discriminator" for them
- *
- * @param discriminator The discriminator value - ignored if {@code null}
- * or empty
- */
- protected AbstractLoggingBean(String discriminator) {
- String name = getClass().getName();
- if (GenericUtils.length(discriminator) > 0) {
- name += "[" + discriminator + "]";
- }
- log = LoggerFactory.getLogger(name);
- }
-
- protected SimplifiedLog getSimplifiedLogger() {
- SimplifiedLog logger;
- synchronized (simplifiedLog) {
- logger = simplifiedLog.get();
- if (logger == null) {
- logger = LoggingUtils.wrap(log);
- }
- }
-
- return logger;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
deleted file mode 100644
index 674ed1a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
+++ /dev/null
@@ -1,549 +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.util.logging;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableMap;
-import java.util.Objects;
-import java.util.TreeMap;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-import java.util.logging.Level;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ReflectionUtils;
-import org.slf4j.Logger;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class LoggingUtils {
-
- private LoggingUtils() {
- throw new UnsupportedOperationException("No instance");
- }
-
- /**
- * Scans using reflection API for all fields that are {@code public static final}
- * that start with the given common prefix (case <U>sensitive</U>) and are of type
- * {@link Number}.
- *
- * @param clazz The {@link Class} to query
- * @param commonPrefix The expected common prefix
- * @return A {@link NavigableMap} of all the matching fields, where key=the field's {@link Integer}
- * value and mapping=the field's name
- * @see #generateMnemonicMap(Class, Predicate)
- */
- public static NavigableMap<Integer, String> generateMnemonicMap(Class<?> clazz, final String commonPrefix) {
- return generateMnemonicMap(clazz, f -> {
- String name = f.getName();
- return name.startsWith(commonPrefix);
- });
- }
-
- /**
- * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
- * that are also accepted by the predicate. Any field that is not such or fail to retrieve
- * its value, or has a duplicate value is <U>silently</U> skipped.
- *
- * @param clazz The {@link Class} to query
- * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
- * (besides being a {@link Number} and {@code public static final}).
- * @return A {@link NavigableMap} of all the matching fields, where key=the field's {@link Integer}
- * value and mapping=the field's name
- * @see #getMnemonicFields(Class, Predicate)
- */
- public static NavigableMap<Integer, String> generateMnemonicMap(Class<?> clazz, Predicate<? super Field> acceptor) {
- Collection<Field> fields = getMnemonicFields(clazz, acceptor);
- if (GenericUtils.isEmpty(fields)) {
- return Collections.emptyNavigableMap();
- }
-
- NavigableMap<Integer, String> result = new TreeMap<>(Comparator.naturalOrder());
- for (Field f : fields) {
- String name = f.getName();
- try {
- Number value = (Number) f.get(null);
- String prev = result.put(NumberUtils.toInteger(value), name);
- if (prev != null) {
- //noinspection UnnecessaryContinue
- continue; // debug breakpoint
- }
- } catch (Exception e) {
- //noinspection UnnecessaryContinue
- continue; // debug breakpoint
- }
- }
-
- return result;
- }
-
- /**
- * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
- * that have a common prefix and whose value is used by several of the other
- * matching fields
- *
- * @param clazz The {@link Class} to query
- * @param commonPrefix The expected common prefix
- * @return A {@link Map} of all the mnemonic fields names whose value is the same as other
- * fields in this map. The key is the field's name and value is its associated opcode.
- * @see #getAmbiguousMenmonics(Class, Predicate)
- */
- public static Map<String, Integer> getAmbiguousMenmonics(Class<?> clazz, String commonPrefix) {
- return getAmbiguousMenmonics(clazz, f -> {
- String name = f.getName();
- return name.startsWith(commonPrefix);
- });
- }
-
- /**
- * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
- * that are also accepted by the predicate and whose value is used by several of the other
- * matching fields
- *
- * @param clazz The {@link Class} to query
- * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
- * (besides being a {@link Number} and {@code public static final}).
- * @return A {@link Map} of all the mnemonic fields names whose value is the same as other
- * fields in this map. The key is the field's name and value is its associated opcode.
- * @see #getMnemonicFields(Class, Predicate)
- */
- public static Map<String, Integer> getAmbiguousMenmonics(Class<?> clazz, Predicate<? super Field> acceptor) {
- Collection<Field> fields = getMnemonicFields(clazz, acceptor);
- if (GenericUtils.isEmpty(fields)) {
- return Collections.emptyMap();
- }
-
- Map<String, Integer> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- Map<Integer, List<String>> opcodesMap = new TreeMap<>(Comparator.naturalOrder());
- for (Field f : fields) {
- String name = f.getName();
- try {
- Number value = (Number) f.get(null);
- Integer key = NumberUtils.toInteger(value);
- List<String> nameList = opcodesMap.get(key);
- if (nameList == null) {
- nameList = new ArrayList<>();
- opcodesMap.put(key, nameList);
- }
- nameList.add(name);
-
- int numOpcodes = nameList.size();
- if (numOpcodes > 1) {
- result.put(name, key);
- if (numOpcodes == 2) { // add the 1st name as well
- result.put(nameList.get(0), key);
- }
- }
- } catch (Exception e) {
- continue; // debug breakpoint
- }
- }
-
- return result;
- }
-
- /**
- * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
- * that are also accepted by the predicate.
- *
- * @param clazz The {@link Class} to query
- * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
- * (besides being a {@link Number} and {@code public static final}).
- * @return A {@link Collection} of all the fields that have satisfied all conditions
- */
- public static Collection<Field> getMnemonicFields(Class<?> clazz, Predicate<? super Field> acceptor) {
- return ReflectionUtils.getMatchingFields(clazz, f -> {
- int mods = f.getModifiers();
- if ((!Modifier.isPublic(mods)) || (!Modifier.isStatic(mods)) || (!Modifier.isFinal(mods))) {
- return false;
- }
-
- Class<?> type = f.getType();
- if (!NumberUtils.isNumericClass(type)) {
- return false;
- }
-
- return acceptor.test(f);
- });
- }
-
- /**
- * Verifies if the given level is above the required threshold for logging.
- *
- * @param level The {@link Level} to evaluate
- * @param threshold The threshold {@link Level}
- * @return {@code true} if the evaluated level is above the required
- * threshold.
- * <P>
- * <B>Note(s):</B>
- * </P>
- * <UL>
- * <LI><P>
- * If either argument is {@code null} then result is {@code false}.
- * </P></LI>
- *
- * <LI><P>
- * If the evaluated level is {@link Level#OFF} then result is {@code false}
- * regardless of the threshold.
- * </P></LI>
- *
- * <LI><P>
- * If the threshold is {@link Level#ALL} and the evaluated level is
- * <U>not</U> {@link Level#OFF} the result is {@code true}.
- * </P></LI>
- *
- * <LI><P>
- * Otherwise, the evaluated level {@link Level#intValue()} must be
- * greater or equal to the threshold.
- * </P></LI>
- * </UL>
- */
- public static boolean isLoggable(Level level, Level threshold) {
- if ((level == null) || (threshold == null)) {
- return false;
- } else if (Level.OFF.equals(level) || Level.OFF.equals(threshold)) {
- return false;
- } else if (Level.ALL.equals(threshold)) {
- return true;
- } else {
- return level.intValue() >= threshold.intValue();
- }
- }
-
- public static SimplifiedLog wrap(final Logger logger) {
- if (logger == null) {
- return SimplifiedLog.EMPTY;
- } else {
- return new SimplifiedLog() {
- @Override
- public void log(Level level, Object message, Throwable t) {
- if (isEnabled(level)) {
- logMessage(logger, level, message, t);
- }
-
- }
-
- @Override
- public boolean isEnabled(Level level) {
- return isLoggable(logger, level);
- }
- };
- }
- }
-
- // NOTE: assume that level enabled has been checked !!!
- public static void logMessage(Logger logger, Level level, Object message, Throwable t) {
- if ((logger == null) || (level == null) || Level.OFF.equals(level)) {
- return;
- } else if (Level.SEVERE.equals(level)) {
- logger.error(Objects.toString(message), t);
- } else if (Level.WARNING.equals(level)) {
- logger.warn(Objects.toString(message), t);
- } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) {
- logger.info(Objects.toString(message), t);
- } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) {
- logger.debug(Objects.toString(message), t);
- } else {
- logger.trace(Objects.toString(message), t);
- }
- }
-
- /**
- * @param logger The {@link Logger} instance - ignored if {@code null}
- * @param level The validate log {@link Level} - ignored if {@code null}
- * @return <P>{@code true} if the level is enabled for the logger. The
- * mapping of the level to the logger is as follows:</P>
- * <UL>
- * <LI>{@link Level#OFF} always returns {@code false}</LI>
- * <LI>{@link Level#SEVERE} returns {@link Logger#isErrorEnabled()}</LI>
- * <LI>{@link Level#WARNING} returns {@link Logger#isWarnEnabled()}</LI>
- * <LI>{@link Level#INFO} and {@link Level#ALL} returns {@link Logger#isInfoEnabled()}</LI>
- * <LI>{@link Level#CONFIG} and {@link Level#FINE} returns {@link Logger#isDebugEnabled()}</LI>
- * <LI>All other levels return {@link Logger#isTraceEnabled()}</LI>
- * </UL>
- */
- public static boolean isLoggable(Logger logger, Level level) {
- if ((logger == null) || (level == null) || Level.OFF.equals(level)) {
- return false;
- } else if (Level.SEVERE.equals(level)) {
- return logger.isErrorEnabled();
- } else if (Level.WARNING.equals(level)) {
- return logger.isWarnEnabled();
- } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) {
- return logger.isInfoEnabled();
- } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) {
- return logger.isDebugEnabled();
- } else {
- return logger.isTraceEnabled();
- }
- }
-
- /**
- * @param <T> Generic message type consumer
- * @param logger The {@link Logger} instance to use
- * @param level The log {@link Level} mapped as follows:</BR>
- *
- * <UL>
- * <LI>{@link Level#OFF} - {@link #nologClosure(Logger)}</LI>
- * <LI>{@link Level#SEVERE} - {@link #errorClosure(Logger)}</LI>
- * <LI>{@link Level#WARNING} - {@link #warnClosure(Logger)}</LI>
- * <LI>{@link Level#INFO}/{@link Level#ALL} - {@link #infoClosure(Logger)}</LI>
- * <LI>{@link Level#CONFIG}/{@link Level#FINE} - {@link #debugClosure(Logger)}</LI>
- * <LI>All others - {@link #traceClosure(Logger)}</LI>
- * </UL>
- * @return A consumer whose {@link Consumer#accept(Object)} method logs the
- * {@link String#valueOf(Object)} value of its argument if the specific level is enabled
- */
- public static <T> Consumer<T> loggingClosure(Logger logger, Level level) {
- return loggingClosure(logger, level, null);
- }
-
- public static <T> Consumer<T> loggingClosure(Logger logger, Level level, Throwable t) {
- Objects.requireNonNull(level, "No level provided");
-
- if (Level.OFF.equals(level)) {
- return nologClosure(logger);
- } else if (Level.SEVERE.equals(level)) {
- return errorClosure(logger, t);
- } else if (Level.WARNING.equals(level)) {
- return warnClosure(logger, t);
- } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) {
- return infoClosure(logger, t);
- } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) {
- return debugClosure(logger, t);
- } else {
- return traceClosure(logger, t);
- }
- }
-
- /**
- * @param <T> Generic message type consumer
- * @param logger The {@link Logger} instance to use
- * @return A consumer whose {@link Consumer#accept(Object)} method logs nothing when invoked
- */
- public static <T> Consumer<T> nologClosure(Logger logger) {
- Objects.requireNonNull(logger, "No logger provided");
- return t -> { /* do nothing */ };
- }
-
- /**
- * @param <T> Generic message type consumer
- * @param logger The {@link Logger} instance to use
- * @return A consumer whose {@link Consumer#accept(Object)} method logs the
- * {@link String#valueOf(Object)} value of its argument if {@link Logger#isErrorEnabled()}
- */
- public static <T> Consumer<T> errorClosure(Logger logger) {
- return errorClosure(logger, null);
- }
-
- /**
- * @param <T> Generic message type consumer
- * @param logger The {@link Logger} instance to use
- * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
- * @return A consumer whose {@link Consumer#accept(Object)} method logs the
- * {@link String#valueOf(Object)} value of its argument if {@link Logger#isErrorEnabled()}
- */
- public static <T> Consumer<T> errorClosure(Logger logger, Throwable thrown) {
- Objects.requireNonNull(logger, "No logger provided");
- return new Consumer<T>() {
- @Override
- public void accept(T input) {
- if (logger.isErrorEnabled()) {
- String msg = String.valueOf(input);
- if (thrown == null) {
- logger.error(msg);
- } else {
- logger.error(msg, thrown);
- }
- }
- }
-
- @Override
- public String toString() {
- return "ERROR";
- }
- };
- }
-
- /**
- * @param <T> Generic message type consumer
- * @param logger The {@link Logger} instance to use
- * @return A consumer whose {@link Consumer#accept(Object)} method logs the
- * {@link String#valueOf(Object)} value of its argument if {@link Logger#isWarnEnabled()}
- */
- public static <T> Consumer<T> warnClosure(Logger logger) {
- return warnClosure(logger, null);
- }
-
- /**
- * @param <T> Generic message type consumer
- * @param logger The {@link Logger} instance to use
- * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
- * @return A consumer whose {@link Consumer#accept(Object)} method logs the {@link String#valueOf(Object)}
- * value of its argument if {@link Logger#isWarnEnabled()}
- */
- public static <T> Consumer<T> warnClosure(Logger logger, Throwable thrown) {
- Objects.requireNonNull(logger, "No logger provided");
- return new Consumer<T>() {
- @Override
- public void accept(T input) {
- if (logger.isWarnEnabled()) {
- String msg = String.valueOf(input);
- if (thrown == null) {
- logger.warn(msg);
- } else {
- logger.warn(msg, thrown);
- }
- }
- }
-
- @Override
- public String toString() {
- return "WARN";
- }
- };
- }
-
- /**
- * @param <T> Generic message type consumer
- * @param logger The {@link Logger} instance to use
- * @return A consumer whose {@link Consumer#accept(Object)} method logs the {@link String#valueOf(Object)}
- * value of its argument if {@link Logger#isInfoEnabled()}
- */
- public static <T> Consumer<T> infoClosure(Logger logger) {
- return infoClosure(logger, null);
- }
-
- /**
- * @param <T> Generic message type consumer
- * @param logger The {@link Logger} instance to use
- * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
- * @return A consumer whose {@link Consumer#accept(Object)} method logs the
- * {@link String#valueOf(Object)} value of its argument if {@link Logger#isInfoEnabled()}
- */
- public static <T> Consumer<T> infoClosure(Logger logger, Throwable thrown) {
- Objects.requireNonNull(logger, "No logger provided");
- return new Consumer<T>() {
- @Override
- public void accept(T input) {
- if (logger.isInfoEnabled()) {
- String msg = String.valueOf(input);
- if (thrown == null) {
- logger.info(msg);
- } else {
- logger.info(msg, thrown);
- }
- }
- }
-
- @Override
- public String toString() {
- return "INFO";
- }
- };
- }
-
- /**
- * @param <T> Generic message type consumer
- * @param logger The {@link Logger} instance to use
- * @return A consumer whose {@link Consumer#accept(Object)} method logs the
- * {@link String#valueOf(Object)} value of its argument if {@link Logger#isDebugEnabled()}
- */
- public static <T> Consumer<T> debugClosure(Logger logger) {
- return debugClosure(logger, null);
- }
-
- /**
- * @param <T> Generic message type consumer
- * @param logger The {@link Logger} instance to use
- * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
- * @return A consumer whose {@link Consumer#accept(Object)} method logs the
- * {@link String#valueOf(Object)} value of its argument if {@link Logger#isDebugEnabled()}
- */
- public static <T> Consumer<T> debugClosure(Logger logger, Throwable thrown) {
- Objects.requireNonNull(logger, "No logger provided");
- return new Consumer<T>() {
- @Override
- public void accept(T input) {
- if (logger.isDebugEnabled()) {
- String msg = String.valueOf(input);
- if (thrown == null) {
- logger.debug(msg);
- } else {
- logger.debug(msg, thrown);
- }
- }
- }
-
- @Override
- public String toString() {
- return "DEBUG";
- }
- };
- }
-
- /**
- * @param <T> Generic message type consumer
- * @param logger The {@link Logger} instance to use
- * @return A consumer whose {@link Consumer#accept(Object)} method logs the
- * {@link String#valueOf(Object)} value of its argument if {@link Logger#isTraceEnabled()}
- */
- public static <T> Consumer<T> traceClosure(Logger logger) {
- return traceClosure(logger, null);
- }
-
- /**
- * @param <T> Generic message type consumer
- * @param logger The {@link Logger} instance to use
- * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
- * @return A consumer whose {@link Consumer#accept(Object)} method logs the
- * {@link String#valueOf(Object)} value of its argument if {@link Logger#isTraceEnabled()}
- */
- public static <T> Consumer<T> traceClosure(Logger logger, Throwable thrown) {
- Objects.requireNonNull(logger, "No logger provided");
- return new Consumer<T>() {
- @Override
- public void accept(T input) {
- if (logger.isTraceEnabled()) {
- String msg = String.valueOf(input);
- if (thrown == null) {
- logger.trace(msg);
- } else {
- logger.trace(msg, thrown);
- }
- }
- }
-
- @Override
- public String toString() {
- return "TRACE";
- }
- };
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java b/sshd-core/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java
deleted file mode 100644
index 58d58ee..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java
+++ /dev/null
@@ -1,55 +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.util.logging;
-
-import java.util.logging.Level;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface SimplifiedLog {
-
- /**
- * An "empty" {@link SimplifiedLog} that does nothing
- */
- SimplifiedLog EMPTY = new SimplifiedLog() {
- @Override
- public boolean isEnabled(Level level) {
- return false;
- }
-
- @Override
- public void log(Level level, Object message, Throwable t) {
- // ignored
- }
-
- @Override
- public String toString() {
- return "EMPTY";
- }
- };
-
- boolean isEnabled(Level level);
-
- default void log(Level level, Object message) {
- log(level, message, null);
- }
-
- void log(Level level, Object message, Throwable t);
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/net/NetworkConnector.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/net/NetworkConnector.java b/sshd-core/src/main/java/org/apache/sshd/common/util/net/NetworkConnector.java
deleted file mode 100644
index df7683a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/net/NetworkConnector.java
+++ /dev/null
@@ -1,90 +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.util.net;
-
-import java.util.concurrent.TimeUnit;
-
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NetworkConnector extends AbstractLoggingBean {
- public static final String DEFAULT_HOST = SshdSocketAddress.LOCALHOST_IPV4;
- public static final long DEFAULT_CONNECT_TIMEOUT = TimeUnit.SECONDS.toMillis(5L);
- public static final long DEFAULT_READ_TIMEOUT = TimeUnit.SECONDS.toMillis(15L);
-
- private String protocol;
- private String host = DEFAULT_HOST;
- private int port;
- private long connectTimeout = DEFAULT_CONNECT_TIMEOUT;
- private long readTimeout = DEFAULT_READ_TIMEOUT;
-
- public NetworkConnector() {
- super();
- }
-
- public String getProtocol() {
- return protocol;
- }
-
- public void setProtocol(String protocol) {
- this.protocol = protocol;
- }
-
- public String getHost() {
- return host;
- }
-
- public void setHost(String host) {
- this.host = host;
- }
-
- public int getPort() {
- return port;
- }
-
- public void setPort(int port) {
- this.port = port;
- }
-
- public long getConnectTimeout() {
- return connectTimeout;
- }
-
- public void setConnectTimeout(long connectTimeout) {
- this.connectTimeout = connectTimeout;
- }
-
- public long getReadTimeout() {
- return readTimeout;
- }
-
- public void setReadTimeout(long readTimeout) {
- this.readTimeout = readTimeout;
- }
-
- @Override
- public String toString() {
- return getProtocol() + "://" + getHost() + ":" + getPort()
- + ";connect=" + getConnectTimeout()
- + ";read=" + getReadTimeout();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java b/sshd-core/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java
deleted file mode 100644
index b465d14..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java
+++ /dev/null
@@ -1,639 +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.util.net;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.NetworkInterface;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Enumeration;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * <P>A simple socket address holding the host name and port number. The reason
- * it does not extend {@link InetSocketAddress} is twofold:</P>
- * <OL>
- * <LI><P>
- * The {@link InetSocketAddress} performs a DNS resolution on the
- * provided host name - which we don't want do use until we want to
- * create a connection using this address (thus the {@link #toInetSocketAddress()}
- * call which executes this query
- * </P></LI>
- *
- * <LI><P>
- * If empty host name is provided we replace it with the <I>any</I>
- * address of 0.0.0.0
- * </P></LI>
- * </OL>
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SshdSocketAddress extends SocketAddress {
- public static final String LOCALHOST_NAME = "localhost";
- public static final String LOCALHOST_IPV4 = "127.0.0.1";
- public static final String IPV4_ANYADDR = "0.0.0.0";
-
- public static final Set<String> WELL_KNOWN_IPV4_ADDRESSES =
- Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(LOCALHOST_IPV4, IPV4_ANYADDR)));
-
- // 10.0.0.0 - 10.255.255.255
- public static final String PRIVATE_CLASS_A_PREFIX = "10.";
- // 172.16.0.0 - 172.31.255.255
- public static final String PRIVATE_CLASS_B_PREFIX = "172.";
- // 192.168.0.0 - 192.168.255.255
- public static final String PRIVATE_CLASS_C_PREFIX = "192.168.";
- // 100.64.0.0 - 100.127.255.255
- public static final String CARRIER_GRADE_NAT_PREFIX = "100.";
- // The IPv4 broadcast address
- public static final String BROADCAST_ADDRESS = "255.255.255.255";
-
- /** Max. number of hex groups (separated by ":") in an IPV6 address */
- public static final int IPV6_MAX_HEX_GROUPS = 8;
-
- /** Max. hex digits in each IPv6 group */
- public static final int IPV6_MAX_HEX_DIGITS_PER_GROUP = 4;
-
- public static final String IPV6_LONG_ANY_ADDRESS = "0:0:0:0:0:0:0:0";
- public static final String IPV6_SHORT_ANY_ADDRESS = "::";
-
- public static final String IPV6_LONG_LOCALHOST = "0:0:0:0:0:0:0:1";
- public static final String IPV6_SHORT_LOCALHOST = "::1";
-
- public static final Set<String> WELL_KNOWN_IPV6_ADDRESSES =
- Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(
- IPV6_LONG_LOCALHOST, IPV6_SHORT_LOCALHOST,
- IPV6_LONG_ANY_ADDRESS, IPV6_SHORT_ANY_ADDRESS)));
-
- /**
- * A dummy placeholder that can be used instead of {@code null}s
- */
- public static final SshdSocketAddress LOCALHOST_ADDRESS = new SshdSocketAddress(LOCALHOST_IPV4, 0);
-
- /**
- * Compares {@link InetAddress}-es according to their {@link InetAddress#getHostAddress()}
- * value case <U>insensitive</U>
- *
- * @see #toAddressString(InetAddress)
- */
- public static final Comparator<InetAddress> BY_HOST_ADDRESS = (a1, a2) -> {
- String n1 = GenericUtils.trimToEmpty(toAddressString(a1));
- String n2 = GenericUtils.trimToEmpty(toAddressString(a2));
- return String.CASE_INSENSITIVE_ORDER.compare(n1, n2);
- };
-
- /**
- * Compares {@link SocketAddress}-es according to their host case <U>insensitive</U>
- * and if equals, then according to their port value (if any)
- *
- * @see #toAddressString(SocketAddress)
- * @see #toAddressPort(SocketAddress)
- */
- public static final Comparator<SocketAddress> BY_HOST_AND_PORT = (a1, a2) -> {
- String n1 = GenericUtils.trimToEmpty(toAddressString(a1));
- String n2 = GenericUtils.trimToEmpty(toAddressString(a2));
- int nRes = String.CASE_INSENSITIVE_ORDER.compare(n1, n2);
- if (nRes != 0) {
- return nRes;
- }
-
- int p1 = toAddressPort(a1);
- int p2 = toAddressPort(a2);
- nRes = Integer.compare(p1, p2);
- if (nRes != 0) {
- return nRes;
- }
-
- return 0;
- };
-
- private static final long serialVersionUID = 6461645947151952729L;
-
- private final String hostName;
- private final int port;
-
- public SshdSocketAddress(int port) {
- this(IPV4_ANYADDR, port);
- }
-
- public SshdSocketAddress(InetSocketAddress addr) {
- Objects.requireNonNull(addr, "No address provided");
-
- String host = addr.getHostString();
- hostName = GenericUtils.isEmpty(host) ? IPV4_ANYADDR : host;
- port = addr.getPort();
- ValidateUtils.checkTrue(port >= 0, "Port must be >= 0: %d", port);
- }
-
- public SshdSocketAddress(String hostName, int port) {
- Objects.requireNonNull(hostName, "Host name may not be null");
- this.hostName = GenericUtils.isEmpty(hostName) ? IPV4_ANYADDR : hostName;
-
- ValidateUtils.checkTrue(port >= 0, "Port must be >= 0: %d", port);
- this.port = port;
- }
-
- public String getHostName() {
- return hostName;
- }
-
- public int getPort() {
- return port;
- }
-
- public InetSocketAddress toInetSocketAddress() {
- return new InetSocketAddress(getHostName(), getPort());
- }
-
- @Override
- public String toString() {
- return getHostName() + ":" + getPort();
- }
-
- protected boolean isEquivalent(SshdSocketAddress that) {
- if (that == null) {
- return false;
- } else if (that == this) {
- return true;
- } else {
- return (this.getPort() == that.getPort())
- && (GenericUtils.safeCompare(this.getHostName(), that.getHostName(), false) == 0);
- }
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == null) {
- return false;
- }
- if (getClass() != o.getClass()) {
- return false;
- }
- return isEquivalent((SshdSocketAddress) o);
- }
-
- @Override
- public int hashCode() {
- return GenericUtils.hashCode(getHostName(), Boolean.FALSE) + getPort();
- }
-
-
- /**
- * Returns the first external network address assigned to this
- * machine or null if one is not found.
- * @return Inet4Address associated with an external interface
- * DevNote: We actually return InetAddress here, as Inet4Addresses are final and cannot be mocked.
- */
- public static InetAddress getFirstExternalNetwork4Address() {
- List<? extends InetAddress> addresses = getExternalNetwork4Addresses();
- return (GenericUtils.size(addresses) > 0) ? addresses.get(0) : null;
- }
-
- /**
- * @return a {@link List} of local network addresses which are not multicast
- * or localhost sorted according to {@link #BY_HOST_ADDRESS}
- */
- public static List<InetAddress> getExternalNetwork4Addresses() {
- List<InetAddress> addresses = new ArrayList<>();
- try {
- for (Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces(); (nets != null) && nets.hasMoreElements();) {
- NetworkInterface netint = nets.nextElement();
- /* TODO - uncomment when 1.5 compatibility no longer required
- if (!netint.isUp()) {
- continue; // ignore non-running interfaces
- }
- */
-
- for (Enumeration<InetAddress> inetAddresses = netint.getInetAddresses(); (inetAddresses != null) && inetAddresses.hasMoreElements();) {
- InetAddress inetAddress = inetAddresses.nextElement();
- if (isValidHostAddress(inetAddress)) {
- addresses.add(inetAddress);
- }
- }
- }
- } catch (SocketException e) {
- // swallow
- }
-
- if (GenericUtils.size(addresses) > 1) {
- Collections.sort(addresses, BY_HOST_ADDRESS);
- }
-
- return addresses;
- }
-
- /**
- * @param addr The {@link InetAddress} to be verified
- * @return <P><code>true</code> if the address is:</P></BR>
- * <UL>
- * <LI>Not {@code null}</LI>
- * <LI>An {@link Inet4Address}</LI>
- * <LI>Not link local</LI>
- * <LI>Not a multicast</LI>
- * <LI>Not a loopback</LI>
- * </UL>
- * @see InetAddress#isLinkLocalAddress()
- * @see InetAddress#isMulticastAddress()
- * @see InetAddress#isMulticastAddress()
- */
- public static boolean isValidHostAddress(InetAddress addr) {
- if (addr == null) {
- return false;
- }
-
- if (addr.isLinkLocalAddress()) {
- return false;
- }
-
- if (addr.isMulticastAddress()) {
- return false;
- }
-
- if (!(addr instanceof Inet4Address)) {
- return false; // TODO add support for IPv6 - see SSHD-746
- }
-
- return !isLoopback(addr);
-
- }
-
- /**
- * @param addr The {@link InetAddress} to be considered
- * @return <code>true</code> if the address is a loopback one.
- * <B>Note:</B> if {@link InetAddress#isLoopbackAddress()}
- * returns <code>false</code> the address <U>string</U> is checked
- * @see #toAddressString(InetAddress)
- * @see #isLoopback(String)
- */
- public static boolean isLoopback(InetAddress addr) {
- if (addr == null) {
- return false;
- }
-
- if (addr.isLoopbackAddress()) {
- return true;
- }
-
- String ip = toAddressString(addr);
- return isLoopback(ip);
- }
-
- /**
- * @param ip IP value to be tested
- * @return <code>true</code> if the IP is "localhost" or
- * "127.x.x.x".
- */
- public static boolean isLoopback(String ip) {
- if (GenericUtils.isEmpty(ip)) {
- return false;
- }
-
- if (LOCALHOST_NAME.equals(ip) || LOCALHOST_IPV4.equals(ip)) {
- return true;
- }
-
- // TODO add support for IPv6 - see SSHD-746
- String[] values = GenericUtils.split(ip, '.');
- if (GenericUtils.length(values) != 4) {
- return false;
- }
-
- for (int index = 0; index < values.length; index++) {
- String val = values[index];
- if (!isValidIPv4AddressComponent(val)) {
- return false;
- }
-
- if (index == 0) {
- int number = Integer.parseInt(val);
- if (number != 127) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- public static SshdSocketAddress toSshdSocketAddress(SocketAddress addr) {
- if (addr == null) {
- return null;
- } else if (addr instanceof SshdSocketAddress) {
- return (SshdSocketAddress) addr;
- } else if (addr instanceof InetSocketAddress) {
- InetSocketAddress isockAddress = (InetSocketAddress) addr;
- return new SshdSocketAddress(isockAddress.getHostName(), isockAddress.getPort());
- } else {
- throw new UnsupportedOperationException("Cannot convert " + addr.getClass().getSimpleName()
- + "=" + addr + " to " + SshdSocketAddress.class.getSimpleName());
- }
- }
-
- public static String toAddressString(SocketAddress addr) {
- if (addr == null) {
- return null;
- } else if (addr instanceof InetSocketAddress) {
- return ((InetSocketAddress) addr).getHostString();
- } else if (addr instanceof SshdSocketAddress) {
- return ((SshdSocketAddress) addr).getHostName();
- } else {
- return addr.toString();
- }
- }
-
- /**
- * Attempts to resolve the port value
- *
- * @param addr The {@link SocketAddress} to examine
- * @return The associated port value - negative if failed to resolve
- */
- public static int toAddressPort(SocketAddress addr) {
- if (addr instanceof InetSocketAddress) {
- return ((InetSocketAddress) addr).getPort();
- } else if (addr instanceof SshdSocketAddress) {
- return ((SshdSocketAddress) addr).getPort();
- } else {
- return -1;
- }
- }
-
- /**
- * <P>Converts a {@code SocketAddress} into an {@link InetSocketAddress} if possible:</P></BR>
- * <UL>
- * <LI>If already an {@link InetSocketAddress} then cast it as such</LI>
- * <LI>If an {@code SshdSocketAddress} then invoke {@link #toInetSocketAddress()}</LI>
- * <LI>Otherwise, throw an exception</LI>
- * </UL>
- *
- * @param remoteAddress The {@link SocketAddress} - ignored if {@code null}
- * @return The {@link InetSocketAddress} instance
- * @throws ClassCastException if argument is not already an {@code InetSocketAddress}
- * or a {@code SshdSocketAddress}
- */
- public static InetSocketAddress toInetSocketAddress(SocketAddress remoteAddress) {
- if (remoteAddress == null) {
- return null;
- } else if (remoteAddress instanceof InetSocketAddress) {
- return (InetSocketAddress) remoteAddress;
- } else if (remoteAddress instanceof SshdSocketAddress) {
- return ((SshdSocketAddress) remoteAddress).toInetSocketAddress();
- } else {
- throw new ClassCastException("Unknown remote address type: " + remoteAddress);
- }
- }
-
- public static String toAddressString(InetAddress addr) {
- String ip = (addr == null) ? null : addr.toString();
- if (GenericUtils.isEmpty(ip)) {
- return null;
- } else {
- return ip.replaceAll(".*/", "");
- }
- }
-
- public static boolean isIPv4Address(String addr) {
- addr = GenericUtils.trimToEmpty(addr);
- if (GenericUtils.isEmpty(addr)) {
- return false;
- }
-
- if (WELL_KNOWN_IPV4_ADDRESSES.contains(addr)) {
- return true;
- }
-
- String[] comps = GenericUtils.split(addr, '.');
- if (GenericUtils.length(comps) != 4) {
- return false;
- }
-
- for (String c : comps) {
- if (!isValidIPv4AddressComponent(c)) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Checks if the address is one of the allocated private blocks
- * @param addr The address string
- * @return {@code true} if this is one of the allocated private
- * blocks. <B>Note:</B> it assumes that the address string is
- * indeed an IPv4 address
- * @see #isIPv4Address(String)
- * @see #PRIVATE_CLASS_A_PREFIX
- * @see #PRIVATE_CLASS_B_PREFIX
- * @see #PRIVATE_CLASS_C_PREFIX
- * @see <A HREF="http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces">Wiki page</A>
- */
- public static boolean isPrivateIPv4Address(String addr) {
- if (GenericUtils.isEmpty(addr)) {
- return false;
- }
-
- if (addr.startsWith(PRIVATE_CLASS_A_PREFIX) || addr.startsWith(PRIVATE_CLASS_C_PREFIX)) {
- return true;
- }
-
- // for 172.x.x.x we need further checks
- if (!addr.startsWith(PRIVATE_CLASS_B_PREFIX)) {
- return false;
- }
-
- int nextCompPos = addr.indexOf('.', PRIVATE_CLASS_B_PREFIX.length());
- if (nextCompPos <= PRIVATE_CLASS_B_PREFIX.length()) {
- return false;
- }
-
- String value = addr.substring(PRIVATE_CLASS_B_PREFIX.length(), nextCompPos);
- if (!isValidIPv4AddressComponent(value)) {
- return false;
- }
-
- int v = Integer.parseInt(value);
- return (v >= 16) && (v <= 31);
- }
-
- /**
- * @param addr The address to be checked
- * @return {@code true} if the address is in the 100.64.0.0/10 range
- * @see <A HREF="http://tools.ietf.org/html/rfc6598">RFC6598</A>
- */
- public static boolean isCarrierGradeNatIPv4Address(String addr) {
- if (GenericUtils.isEmpty(addr)) {
- return false;
- }
-
- if (!addr.startsWith(CARRIER_GRADE_NAT_PREFIX)) {
- return false;
- }
-
- int nextCompPos = addr.indexOf('.', CARRIER_GRADE_NAT_PREFIX.length());
- if (nextCompPos <= CARRIER_GRADE_NAT_PREFIX.length()) {
- return false;
- }
-
- String value = addr.substring(CARRIER_GRADE_NAT_PREFIX.length(), nextCompPos);
- if (!isValidIPv4AddressComponent(value)) {
- return false;
- }
-
- int v = Integer.parseInt(value);
- return (v >= 64) && (v <= 127);
- }
-
- /**
- * <P>Checks if the provided argument is a valid IPv4 address component:</P></BR>
- * <UL>
- * <LI>Not {@code null}/empty</LI>
- * <LI>Has at most 3 <U>digits</U></LI>
- * <LI>Its value is ≤ 255</LI>
- * </UL>
- * @param c The {@link CharSequence} to be validate
- * @return {@code true} if valid IPv4 address component
- */
- public static boolean isValidIPv4AddressComponent(CharSequence c) {
- if (GenericUtils.isEmpty(c) || (c.length() > 3)) {
- return false;
- }
-
- char ch = c.charAt(0);
- if ((ch < '0') || (ch > '9')) {
- return false;
- }
-
- if (!NumberUtils.isIntegerNumber(c)) {
- return false;
- }
-
- int v = Integer.parseInt(c.toString());
- return (v >= 0) && (v <= 255);
- }
-
- // Based on org.apache.commons.validator.routines.InetAddressValidator#isValidInet6Address
- public static boolean isIPv6Address(String address) {
- address = GenericUtils.trimToEmpty(address);
- if (GenericUtils.isEmpty(address)) {
- return false;
- }
-
- if (WELL_KNOWN_IPV6_ADDRESSES.contains(address)) {
- return true;
- }
-
- boolean containsCompressedZeroes = address.contains("::");
- if (containsCompressedZeroes && (address.indexOf("::") != address.lastIndexOf("::"))) {
- return false;
- }
-
- if (((address.indexOf(':') == 0) && (!address.startsWith("::")))
- || (address.endsWith(":") && (!address.endsWith("::")))) {
- return false;
- }
-
- String[] splitOctets = GenericUtils.split(address, ':');
- List<String> octetList = new ArrayList<>(Arrays.asList(splitOctets));
- if (containsCompressedZeroes) {
- if (address.endsWith("::")) {
- // String.split() drops ending empty segments
- octetList.add("");
- } else if (address.startsWith("::") && (!octetList.isEmpty())) {
- octetList.remove(0);
- }
- }
-
- int numOctests = octetList.size();
- if (numOctests > IPV6_MAX_HEX_GROUPS) {
- return false;
- }
-
- int validOctets = 0;
- int emptyOctets = 0; // consecutive empty chunks
- for (int index = 0; index < numOctests; index++) {
- String octet = octetList.get(index);
- int pos = octet.indexOf('%'); // is it a zone index
- if (pos >= 0) {
- // zone index must come last
- if (index != (numOctests - 1)) {
- return false;
- }
-
- octet = (pos > 0) ? octet.substring(0, pos) : "";
- }
-
- int octetLength = octet.length();
- if (octetLength == 0) {
- emptyOctets++;
- if (emptyOctets > 1) {
- return false;
- }
-
- validOctets++;
- continue;
- }
-
- emptyOctets = 0;
-
- // Is last chunk an IPv4 address?
- if ((index == (numOctests - 1)) && (octet.indexOf('.') > 0)) {
- if (!isIPv4Address(octet)) {
- return false;
- }
- validOctets += 2;
- continue;
- }
-
- if (octetLength > IPV6_MAX_HEX_DIGITS_PER_GROUP) {
- return false;
- }
-
- int octetInt = 0;
- try {
- octetInt = Integer.parseInt(octet, 16);
- } catch (NumberFormatException e) {
- return false;
- }
-
- if ((octetInt < 0) || (octetInt > 0x000ffff)) {
- return false;
- }
-
- validOctets++;
- }
-
- if ((validOctets > IPV6_MAX_HEX_GROUPS)
- || ((validOctets < IPV6_MAX_HEX_GROUPS) && (!containsCompressedZeroes))) {
- return false;
- }
- return true;
- }
-}
[41/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/mac/BaseMac.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/mac/BaseMac.java b/sshd-common/src/main/java/org/apache/sshd/common/mac/BaseMac.java
new file mode 100644
index 0000000..e1681d4
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/mac/BaseMac.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.mac;
+
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Base class for <code>Mac</code> implementations based on the JCE provider.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BaseMac implements Mac {
+
+ private final String algorithm;
+ private final int defbsize;
+ private final int bsize;
+ private final byte[] tmp;
+ private javax.crypto.Mac mac;
+ private String s;
+
+ public BaseMac(String algorithm, int bsize, int defbsize) {
+ this.algorithm = algorithm;
+ this.bsize = bsize;
+ this.defbsize = defbsize;
+ this.tmp = new byte[defbsize];
+ }
+
+ @Override
+ public final String getAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public final int getBlockSize() {
+ return bsize;
+ }
+
+ @Override
+ public final int getDefaultBlockSize() {
+ return defbsize;
+ }
+
+ @Override
+ public void init(byte[] key) throws Exception {
+ if (key.length > defbsize) {
+ byte[] tmp = new byte[defbsize];
+ System.arraycopy(key, 0, tmp, 0, defbsize);
+ key = tmp;
+ }
+
+ SecretKeySpec skey = new SecretKeySpec(key, algorithm);
+ mac = SecurityUtils.getMac(algorithm);
+ mac.init(skey);
+ }
+
+ @Override
+ public void updateUInt(long i) {
+ tmp[0] = (byte) (i >>> 24);
+ tmp[1] = (byte) (i >>> 16);
+ tmp[2] = (byte) (i >>> 8);
+ tmp[3] = (byte) i;
+ update(tmp, 0, 4);
+ }
+
+ @Override
+ public void update(byte buf[], int offset, int len) {
+ mac.update(buf, offset, len);
+ }
+
+ @Override
+ public void doFinal(byte[] buf, int offset) throws Exception {
+ int blockSize = getBlockSize();
+ int defaultSize = getDefaultBlockSize();
+ if (blockSize != defaultSize) {
+ mac.doFinal(tmp, 0);
+ System.arraycopy(tmp, 0, buf, offset, blockSize);
+ } else {
+ mac.doFinal(buf, offset);
+ }
+ }
+
+ @Override
+ public String toString() {
+ synchronized (this) {
+ if (s == null) {
+ s = getClass().getSimpleName() + "[" + getAlgorithm() + "] - "
+ + " block=" + getBlockSize() + "/" + getDefaultBlockSize() + " bytes";
+ }
+ }
+
+ return s;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java b/sshd-common/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
new file mode 100644
index 0000000..5a4528d
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.mac;
+
+import 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.TreeMap;
+
+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 macs
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum BuiltinMacs implements MacFactory {
+ hmacmd5(Constants.HMAC_MD5, "HmacMD5", 16, 16),
+ hmacmd596(Constants.HMAC_MD5_96, "HmacMD5", 12, 16),
+ hmacsha1(Constants.HMAC_SHA1, "HmacSHA1", 20, 20),
+ hmacsha196(Constants.HMAC_SHA1_96, "HmacSHA1", 12, 20),
+ /** See <A HREF="https://tools.ietf.org/html/rfc6668">RFC 6668</A> */
+ hmacsha256(Constants.HMAC_SHA2_256, "HmacSHA256", 32, 32),
+ /** See <A HREF="https://tools.ietf.org/html/rfc6668">RFC 6668</A> */
+ hmacsha512(Constants.HMAC_SHA2_512, "HmacSHA512", 64, 64);
+
+ public static final Set<BuiltinMacs> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(BuiltinMacs.class));
+
+ private static final Map<String, MacFactory> EXTENSIONS =
+ new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+ private final String factoryName;
+ private final String algorithm;
+ private final int defbsize;
+ private final int bsize;
+
+ BuiltinMacs(String factoryName, String algorithm, int bsize, int defbsize) {
+ this.factoryName = factoryName;
+ this.algorithm = algorithm;
+ this.bsize = bsize;
+ this.defbsize = defbsize;
+ }
+
+ @Override
+ public Mac create() {
+ return new BaseMac(getAlgorithm(), getBlockSize(), getDefaultBlockSize());
+ }
+
+ @Override
+ public final String getName() {
+ return factoryName;
+ }
+
+ @Override
+ public final String getAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public final int getBlockSize() {
+ return bsize;
+ }
+
+ @Override
+ public final int getDefaultBlockSize() {
+ return defbsize;
+ }
+
+ @Override
+ public final boolean isSupported() {
+ return true;
+ }
+
+ @Override
+ public final String toString() {
+ return getName();
+ }
+
+ /**
+ * 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(MacFactory 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 NavigableSet} of the currently registered extensions, sorted
+ * according to the factory name (case <U>insensitive</U>)
+ */
+ public static NavigableSet<MacFactory> 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 MacFactory unregisterExtension(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ synchronized (EXTENSIONS) {
+ return EXTENSIONS.remove(name);
+ }
+ }
+
+ /**
+ * @param s The {@link Enum}'s name - ignored if {@code null}/empty
+ * @return The matching {@link org.apache.sshd.common.mac.BuiltinMacs} whose {@link Enum#name()} matches
+ * (case <U>insensitive</U>) the provided argument - {@code null} if no match
+ */
+ public static BuiltinMacs fromString(String s) {
+ if (GenericUtils.isEmpty(s)) {
+ return null;
+ }
+
+ for (BuiltinMacs c : VALUES) {
+ if (s.equalsIgnoreCase(c.name())) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param factory The {@link org.apache.sshd.common.NamedFactory} for the MAC - ignored if {@code null}
+ * @return The matching {@link org.apache.sshd.common.mac.BuiltinMacs} whose factory name matches
+ * (case <U>insensitive</U>) the digest factory name
+ * @see #fromFactoryName(String)
+ */
+ public static BuiltinMacs fromFactory(NamedFactory<Mac> 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 BuiltinMacs} whose factory name matches
+ * (case <U>insensitive</U>) the provided name - {@code null} if no match
+ */
+ public static BuiltinMacs fromFactoryName(String name) {
+ return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+ }
+
+ /**
+ * @param macs A comma-separated list of MACs' 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 parseMacsList(String macs) {
+ return parseMacsList(GenericUtils.split(macs, ','));
+ }
+
+ public static ParseResult parseMacsList(String... macs) {
+ return parseMacsList(GenericUtils.isEmpty((Object[]) macs) ? Collections.emptyList() : Arrays.asList(macs));
+ }
+
+ public static ParseResult parseMacsList(Collection<String> macs) {
+ if (GenericUtils.isEmpty(macs)) {
+ return ParseResult.EMPTY;
+ }
+
+ List<MacFactory> factories = new ArrayList<>(macs.size());
+ List<String> unknown = Collections.emptyList();
+ for (String name : macs) {
+ MacFactory m = resolveFactory(name);
+ if (m != null) {
+ factories.add(m);
+ } 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 MacFactory resolveFactory(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ MacFactory m = fromFactoryName(name);
+ if (m != null) {
+ return m;
+ }
+
+ synchronized (EXTENSIONS) {
+ return EXTENSIONS.get(name);
+ }
+ }
+
+ public static final class ParseResult extends NamedFactoriesListParseResult<Mac, MacFactory> {
+ public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
+
+ public ParseResult(List<MacFactory> parsed, List<String> unsupported) {
+ super(parsed, unsupported);
+ }
+ }
+
+ public static final class Constants {
+ public static final String HMAC_MD5 = "hmac-md5";
+ public static final String HMAC_MD5_96 = "hmac-md5-96";
+ public static final String HMAC_SHA1 = "hmac-sha1";
+ public static final String HMAC_SHA1_96 = "hmac-sha1-96";
+ public static final String HMAC_SHA2_256 = "hmac-sha2-256";
+ public static final String HMAC_SHA2_512 = "hmac-sha2-512";
+
+ private Constants() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/mac/Mac.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/mac/Mac.java b/sshd-common/src/main/java/org/apache/sshd/common/mac/Mac.java
new file mode 100644
index 0000000..4b80447
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/mac/Mac.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.mac;
+
+import org.apache.sshd.common.util.NumberUtils;
+
+/**
+ * Message Authentication Code for use in SSH.
+ * It usually wraps a javax.crypto.Mac class.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Mac extends MacInformation {
+ void init(byte[] key) throws Exception;
+
+ default void update(byte[] buf) {
+ update(buf, 0, NumberUtils.length(buf));
+ }
+
+ void update(byte[] buf, int start, int len);
+
+ void updateUInt(long foo);
+
+ default byte[] doFinal() throws Exception {
+ int blockSize = getBlockSize();
+ byte[] buf = new byte[blockSize];
+ doFinal(buf);
+ return buf;
+ }
+
+ default void doFinal(byte[] buf) throws Exception {
+ doFinal(buf, 0);
+ }
+
+ void doFinal(byte[] buf, int offset) throws Exception;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/mac/MacFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/mac/MacFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/mac/MacFactory.java
new file mode 100644
index 0000000..4463600
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/mac/MacFactory.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.mac;
+
+import org.apache.sshd.common.BuiltinFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+// CHECKSTYLE:OFF
+public interface MacFactory extends MacInformation, BuiltinFactory<Mac> {
+ // 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/mac/MacInformation.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/mac/MacInformation.java b/sshd-common/src/main/java/org/apache/sshd/common/mac/MacInformation.java
new file mode 100644
index 0000000..583165f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/mac/MacInformation.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.mac;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface MacInformation {
+ /**
+ * @return MAC algorithm name
+ */
+ String getAlgorithm();
+
+ /**
+ * @return MAC output block size in bytes - may be less than the default
+ * - e.g., MD5-96
+ */
+ int getBlockSize();
+
+ /**
+ * @return The "natural" MAC block size in bytes
+ */
+ int getDefaultBlockSize();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/mac/package.html
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/mac/package.html b/sshd-common/src/main/java/org/apache/sshd/common/mac/package.html
new file mode 100644
index 0000000..52a90ca
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/mac/package.html
@@ -0,0 +1,25 @@
+<!--
+ 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.
+-->
+<html>
+<head>
+</head>
+<body>
+
+<a href="{@docRoot}/org/apache/sshd/common/mac/Mac.html"><code>Mac</code></a> implementations.
+
+</body>
+</html>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/random/AbstractRandom.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/random/AbstractRandom.java b/sshd-common/src/main/java/org/apache/sshd/common/random/AbstractRandom.java
new file mode 100644
index 0000000..2a1f825
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/random/AbstractRandom.java
@@ -0,0 +1,34 @@
+/*
+ * 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.random;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractRandom implements Random {
+ protected AbstractRandom() {
+ super();
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/random/AbstractRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/random/AbstractRandomFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/random/AbstractRandomFactory.java
new file mode 100644
index 0000000..c1d7893
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/random/AbstractRandomFactory.java
@@ -0,0 +1,43 @@
+/*
+ * 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.random;
+
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractRandomFactory implements RandomFactory {
+ private final String name;
+
+ protected AbstractRandomFactory(String name) {
+ this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name");
+ }
+
+ @Override
+ public final String getName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/random/JceRandom.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/random/JceRandom.java b/sshd-common/src/main/java/org/apache/sshd/common/random/JceRandom.java
new file mode 100644
index 0000000..ba050e6
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/random/JceRandom.java
@@ -0,0 +1,60 @@
+/*
+ * 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.random;
+
+import java.security.SecureRandom;
+
+/**
+ * A <code>Random</code> implementation using the built-in {@link SecureRandom} PRNG.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class JceRandom extends AbstractRandom {
+ public static final String NAME = "JCE";
+
+ private byte[] tmp = new byte[16];
+ private final SecureRandom random = new SecureRandom();
+
+ public JceRandom() {
+ super();
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public synchronized void fill(byte[] foo, int start, int len) {
+ if ((start == 0) && (len == foo.length)) {
+ random.nextBytes(foo);
+ } else {
+ if (len > tmp.length) {
+ tmp = new byte[len];
+ }
+ random.nextBytes(tmp);
+ System.arraycopy(tmp, 0, foo, start, len);
+ }
+ }
+
+ @Override
+ public synchronized int random(int n) {
+ return random.nextInt(n);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/random/JceRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/random/JceRandomFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/random/JceRandomFactory.java
new file mode 100644
index 0000000..450a85e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/random/JceRandomFactory.java
@@ -0,0 +1,42 @@
+/*
+ * 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.random;
+
+/**
+ * Named factory for the JCE <code>Random</code>
+ */
+public class JceRandomFactory extends AbstractRandomFactory {
+ public static final String NAME = "default";
+ public static final JceRandomFactory INSTANCE = new JceRandomFactory();
+
+ public JceRandomFactory() {
+ super(NAME);
+ }
+
+ @Override
+ public boolean isSupported() {
+ return true;
+ }
+
+ @Override
+ public Random create() {
+ return new JceRandom();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/random/Random.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/random/Random.java b/sshd-common/src/main/java/org/apache/sshd/common/random/Random.java
new file mode 100644
index 0000000..6e597ef
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/random/Random.java
@@ -0,0 +1,56 @@
+/*
+ * 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.random;
+
+import org.apache.sshd.common.NamedResource;
+
+/**
+ * A pseudo random number generator.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Random extends NamedResource {
+ /**
+ * Fill the buffer with random values
+ *
+ * @param bytes The bytes to fill
+ * @see #fill(byte[], int, int)
+ */
+ default void fill(byte[] bytes) {
+ fill(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Fill part of bytes with random values.
+ *
+ * @param bytes byte array to be filled.
+ * @param start index to start filling at.
+ * @param len length of segment to fill.
+ */
+ void fill(byte[] bytes, int start, int len);
+
+ /**
+ * Returns a pseudo-random uniformly distributed {@code int}
+ * in the half-open range [0, n).
+ *
+ * @param n The range upper limit
+ * @return The randomly selected value in the range
+ */
+ int random(int n);
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/random/RandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/random/RandomFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/random/RandomFactory.java
new file mode 100644
index 0000000..dded06f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/random/RandomFactory.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.random;
+
+import org.apache.sshd.common.BuiltinFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+// CHECKSTYLE:OFF
+public interface RandomFactory extends BuiltinFactory<Random> {
+ // 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/random/SingletonRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java
new file mode 100644
index 0000000..ce24112
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java
@@ -0,0 +1,70 @@
+/*
+ * 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.random;
+
+import java.util.Objects;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
+
+/**
+ * A random factory wrapper that uses a single random instance.
+ * The underlying random instance has to be thread safe.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SingletonRandomFactory extends AbstractRandom implements RandomFactory {
+
+ private final NamedFactory<Random> factory;
+ private final Random random;
+
+ public SingletonRandomFactory(NamedFactory<Random> factory) {
+ this.factory = Objects.requireNonNull(factory, "No factory");
+ this.random = Objects.requireNonNull(factory.create(), "No random instance created");
+ }
+
+ @Override
+ public boolean isSupported() {
+ if (factory instanceof OptionalFeature) {
+ return ((OptionalFeature) factory).isSupported();
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ public void fill(byte[] bytes, int start, int len) {
+ random.fill(bytes, start, len);
+ }
+
+ @Override
+ public int random(int max) {
+ return random.random(max);
+ }
+
+ @Override
+ public String getName() {
+ return factory.getName();
+ }
+
+ @Override
+ public Random create() {
+ return this;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/random/package.html
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/random/package.html b/sshd-common/src/main/java/org/apache/sshd/common/random/package.html
new file mode 100644
index 0000000..0e94d7f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/random/package.html
@@ -0,0 +1,25 @@
+<!--
+ 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.
+-->
+<html>
+<head>
+</head>
+<body>
+
+<a href="{@docRoot}/org/apache/sshd/common/random/Random.html"><code>Random</code></a> implementations.
+
+</body>
+</html>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
new file mode 100644
index 0000000..ef06d15
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.signature;
+
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Useful base class for {@link Signature} implementation
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractSignature implements Signature {
+ private java.security.Signature signatureInstance;
+ private final String algorithm;
+
+ protected AbstractSignature(String algorithm) {
+ this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No signature algorithm specified");
+ }
+
+ @Override
+ public final String getAlgorithm() {
+ return algorithm;
+ }
+
+ /**
+ * Initializes the internal signature instance
+ *
+ * @param algo The signature's algorithm
+ * @param forSigning If {@code true} then it is being initialized for signing,
+ * otherwise for verifying a signature
+ * @return The {@link java.security.Signature} instance
+ * @throws GeneralSecurityException if failed to initialize
+ */
+ protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
+ return SecurityUtils.getSignature(algo);
+ }
+
+ /**
+ * @return The current {@link java.security.Signature} instance
+ * - {@code null} if not initialized
+ * @see #doInitSignature(String, boolean)
+ */
+ protected java.security.Signature getSignature() {
+ return signatureInstance;
+ }
+
+ @Override
+ public byte[] sign() throws Exception {
+ java.security.Signature signature = Objects.requireNonNull(getSignature(), "Signature not initialized");
+ return signature.sign();
+ }
+
+ @Override
+ public void initVerifier(PublicKey key) throws Exception {
+ String algo = getAlgorithm();
+ signatureInstance = Objects.requireNonNull(doInitSignature(algo, false), "No signature instance create");
+ signatureInstance.initVerify(Objects.requireNonNull(key, "No public key provided"));
+ }
+
+ @Override
+ public void initSigner(PrivateKey key) throws Exception {
+ String algo = getAlgorithm();
+ signatureInstance = Objects.requireNonNull(doInitSignature(algo, true), "No signature instance create");
+ signatureInstance.initSign(Objects.requireNonNull(key, "No private key provided"));
+ }
+
+ @Override
+ public void update(byte[] hash, int off, int len) throws Exception {
+ java.security.Signature signature = Objects.requireNonNull(getSignature(), "Signature not initialized");
+ signature.update(hash, off, len);
+ }
+
+ /**
+ * Makes an attempt to detect if the signature is encoded or pure data
+ *
+ * @param sig The original signature
+ * @return A {@link SimpleImmutableEntry} where first value is the key type and second
+ * value is the data - {@code null} if not encoded
+ */
+ protected SimpleImmutableEntry<String, byte[]> extractEncodedSignature(byte[] sig) {
+ final int dataLen = NumberUtils.length(sig);
+ // if it is encoded then we must have at least 2 UINT32 values
+ if (dataLen < (2 * Integer.BYTES)) {
+ return null;
+ }
+
+ long keyTypeLen = BufferUtils.getUInt(sig, 0, dataLen);
+ // after the key type we MUST have data bytes
+ if (keyTypeLen >= (dataLen - Integer.BYTES)) {
+ return null;
+ }
+
+ int keyTypeStartPos = Integer.BYTES;
+ int keyTypeEndPos = keyTypeStartPos + (int) keyTypeLen;
+ int remainLen = dataLen - keyTypeEndPos;
+ // must have UINT32 with the data bytes length
+ if (remainLen < Integer.BYTES) {
+ return null;
+ }
+
+ long dataBytesLen = BufferUtils.getUInt(sig, keyTypeEndPos, remainLen);
+ // make sure reported number of bytes does not exceed available
+ if (dataBytesLen > (remainLen - Integer.BYTES)) {
+ return null;
+ }
+
+ String keyType = new String(sig, keyTypeStartPos, (int) keyTypeLen, StandardCharsets.UTF_8);
+ byte[] data = new byte[(int) dataBytesLen];
+ System.arraycopy(sig, keyTypeEndPos + Integer.BYTES, data, 0, (int) dataBytesLen);
+ return new SimpleImmutableEntry<>(keyType, data);
+ }
+
+ protected boolean doVerify(byte[] data) throws SignatureException {
+ java.security.Signature signature = Objects.requireNonNull(getSignature(), "Signature not initialized");
+ return signature.verify(data);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" + getAlgorithm() + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
new file mode 100644
index 0000000..7d4e1e2
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
@@ -0,0 +1,305 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.signature;
+
+import java.security.spec.ECParameterSpec;
+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.TreeMap;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.NamedFactoriesListParseResult;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Provides easy access to the currently implemented signatures
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum BuiltinSignatures implements SignatureFactory {
+ dsa(KeyPairProvider.SSH_DSS) {
+ @Override
+ public Signature create() {
+ return new SignatureDSA();
+ }
+ },
+ rsa(KeyPairProvider.SSH_RSA) {
+ @Override
+ public Signature create() {
+ return new SignatureRSA();
+ }
+ },
+ nistp256(KeyPairProvider.ECDSA_SHA2_NISTP256) {
+ @Override
+ public Signature create() {
+ return new SignatureECDSA.SignatureECDSA256();
+ }
+
+ @Override
+ public boolean isSupported() {
+ return SecurityUtils.isECCSupported();
+ }
+ },
+ nistp384(KeyPairProvider.ECDSA_SHA2_NISTP384) {
+ @Override
+ public Signature create() {
+ return new SignatureECDSA.SignatureECDSA384();
+ }
+
+ @Override
+ public boolean isSupported() {
+ return SecurityUtils.isECCSupported();
+ }
+ },
+ nistp521(KeyPairProvider.ECDSA_SHA2_NISTP521) {
+ @Override
+ public Signature create() {
+ return new SignatureECDSA.SignatureECDSA521();
+ }
+
+ @Override
+ public boolean isSupported() {
+ return SecurityUtils.isECCSupported();
+ }
+ },
+ ed25519(KeyPairProvider.SSH_ED25519) {
+ @Override
+ public Signature create() {
+ return SecurityUtils.getEDDSASigner();
+ }
+
+ @Override
+ public boolean isSupported() {
+ return SecurityUtils.isEDDSACurveSupported();
+ }
+ };
+
+ public static final Set<BuiltinSignatures> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(BuiltinSignatures.class));
+
+ private static final Map<String, SignatureFactory> EXTENSIONS =
+ new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+ private final String factoryName;
+
+ BuiltinSignatures(String facName) {
+ factoryName = facName;
+ }
+
+ public static Signature getByCurveSize(ECParameterSpec params) {
+ int curveSize = ECCurves.getCurveSize(params);
+ if (curveSize <= 256) {
+ return nistp256.create();
+ } else if (curveSize <= 384) {
+ return nistp384.create();
+ } else {
+ return nistp521.create();
+ }
+ }
+
+ @Override
+ public final String getName() {
+ return factoryName;
+ }
+
+ @Override
+ public final String toString() {
+ return getName();
+ }
+
+ @Override
+ public boolean isSupported() {
+ return true;
+ }
+
+ /**
+ * 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(SignatureFactory 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 NavigableSet} of the currently registered extensions, sorted
+ * according to the factory name (case <U>insensitive</U>)
+ */
+ public static NavigableSet<SignatureFactory> 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 SignatureFactory unregisterExtension(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ synchronized (EXTENSIONS) {
+ return EXTENSIONS.remove(name);
+ }
+ }
+
+ /**
+ * @param s The {@link Enum}'s name - ignored if {@code null}/empty
+ * @return The matching {@link org.apache.sshd.common.signature.BuiltinSignatures} whose {@link Enum#name()} matches
+ * (case <U>insensitive</U>) the provided argument - {@code null} if no match
+ */
+ public static BuiltinSignatures fromString(String s) {
+ if (GenericUtils.isEmpty(s)) {
+ return null;
+ }
+
+ for (BuiltinSignatures c : VALUES) {
+ if (s.equalsIgnoreCase(c.name())) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param factory The {@link org.apache.sshd.common.NamedFactory} for the signature - ignored if {@code null}
+ * @return The matching {@link org.apache.sshd.common.signature.BuiltinSignatures} whose factory name matches
+ * (case <U>insensitive</U>) the digest factory name
+ * @see #fromFactoryName(String)
+ */
+ public static BuiltinSignatures fromFactory(NamedFactory<Signature> 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 BuiltinSignatures} whose factory name matches
+ * (case <U>insensitive</U>) the provided name - {@code null} if no match
+ */
+ public static BuiltinSignatures fromFactoryName(String name) {
+ return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+ }
+
+ /**
+ * @param sigs A comma-separated list of signatures' names - ignored
+ * if {@code null}/empty
+ * @return A {@link ParseResult} of all the {@link NamedFactory} whose
+ * name appears in the string and represent a built-in signature. Any
+ * 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
+ */
+ public static ParseResult parseSignatureList(String sigs) {
+ return parseSignatureList(GenericUtils.split(sigs, ','));
+ }
+
+ public static ParseResult parseSignatureList(String... sigs) {
+ return parseSignatureList(GenericUtils.isEmpty((Object[]) sigs) ? Collections.emptyList() : Arrays.asList(sigs));
+ }
+
+ public static ParseResult parseSignatureList(Collection<String> sigs) {
+ if (GenericUtils.isEmpty(sigs)) {
+ return ParseResult.EMPTY;
+ }
+
+ List<SignatureFactory> factories = new ArrayList<>(sigs.size());
+ List<String> unknown = Collections.emptyList();
+ for (String name : sigs) {
+ SignatureFactory s = resolveFactory(name);
+ if (s != null) {
+ factories.add(s);
+ } 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 SignatureFactory resolveFactory(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ SignatureFactory s = fromFactoryName(name);
+ if (s != null) {
+ return s;
+ }
+
+ synchronized (EXTENSIONS) {
+ return EXTENSIONS.get(name);
+ }
+ }
+
+ /**
+ * Holds the result of the {@link BuiltinSignatures#parseSignatureList(String)}
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+ public static final class ParseResult extends NamedFactoriesListParseResult<Signature, SignatureFactory> {
+ public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
+
+ public ParseResult(List<SignatureFactory> parsed, List<String> unsupported) {
+ super(parsed, unsupported);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/Signature.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/Signature.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/Signature.java
new file mode 100644
index 0000000..fd88a1d
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/Signature.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.signature;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+import org.apache.sshd.common.util.NumberUtils;
+
+/**
+ * Signature interface for SSH used to sign or verify packets
+ * Usually wraps a javax.crypto.Signature object
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Signature {
+ /**
+ * @return The signature algorithm name
+ */
+ String getAlgorithm();
+
+ /**
+ * @param key The {@link PublicKey} to be used for verifying signatures
+ * @throws Exception If failed to initialize
+ */
+ void initVerifier(PublicKey key) throws Exception;
+
+ /**
+ * @param key The {@link PrivateKey} to be used for signing
+ * @throws Exception If failed to initialize
+ */
+ void initSigner(PrivateKey key) throws Exception;
+
+ /**
+ * Update the computed signature with the given data
+ *
+ * @param hash The hash data buffer
+ * @throws Exception If failed to update
+ * @see #update(byte[], int, int)
+ */
+ default void update(byte[] hash) throws Exception {
+ update(hash, 0, NumberUtils.length(hash));
+ }
+
+ /**
+ * Update the computed signature with the given data
+ *
+ * @param hash The hash data buffer
+ * @param off Offset of hash data in buffer
+ * @param len Length of hash data
+ * @throws Exception If failed to update
+ */
+ void update(byte[] hash, int off, int len) throws Exception;
+
+ /**
+ * Verify against the given signature
+ *
+ * @param sig The signed data
+ * @return {@code true} if signature is valid
+ * @throws Exception If failed to extract signed data for validation
+ */
+ boolean verify(byte[] sig) throws Exception;
+
+ /**
+ * Compute the signature
+ *
+ * @return The signature value
+ * @throws Exception If failed to calculate the signature
+ */
+ byte[] sign() throws Exception;
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
new file mode 100644
index 0000000..1f552bd
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.signature;
+
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.security.SignatureException;
+import java.util.Map;
+
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.io.der.DERParser;
+import org.apache.sshd.common.util.io.der.DERWriter;
+
+
+/**
+ * DSA <code>Signature</code>
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-6.6">RFC4253 section 6.6</A>
+ */
+public class SignatureDSA extends AbstractSignature {
+ public static final String DEFAULT_ALGORITHM = "SHA1withDSA";
+
+ public static final int DSA_SIGNATURE_LENGTH = 40;
+ // result must be 40 bytes, but length of r and s may not exceed 20 bytes
+ public static final int MAX_SIGNATURE_VALUE_LENGTH = DSA_SIGNATURE_LENGTH / 2;
+
+ public SignatureDSA() {
+ this(DEFAULT_ALGORITHM);
+ }
+
+ protected SignatureDSA(String algorithm) {
+ super(algorithm);
+ }
+
+ @Override
+ public byte[] sign() throws Exception {
+ byte[] sig = super.sign();
+
+ try (DERParser parser = new DERParser(sig)) {
+ int type = parser.read();
+ if (type != 0x30) {
+ throw new StreamCorruptedException("Invalid signature format - not a DER SEQUENCE: 0x" + Integer.toHexString(type));
+ }
+
+ // length of remaining encoding of the 2 integers
+ int remainLen = parser.readLength();
+ /*
+ * There are supposed to be 2 INTEGERs, each encoded with:
+ *
+ * - one byte representing the fact that it is an INTEGER
+ * - one byte of the integer encoding length
+ * - at least one byte of integer data (zero length is not an option)
+ */
+ if (remainLen < (2 * 3)) {
+ throw new StreamCorruptedException("Invalid signature format - not enough encoded data length: " + remainLen);
+ }
+
+ BigInteger r = parser.readBigInteger();
+ BigInteger s = parser.readBigInteger();
+
+ byte[] result = new byte[DSA_SIGNATURE_LENGTH];
+ putBigInteger(r, result, 0);
+ putBigInteger(s, result, MAX_SIGNATURE_VALUE_LENGTH);
+ return result;
+ }
+ }
+
+ public static void putBigInteger(BigInteger value, byte[] result, int offset) {
+ byte[] data = value.toByteArray();
+ boolean maxExceeded = data.length > MAX_SIGNATURE_VALUE_LENGTH;
+ int dstOffset = maxExceeded ? 0 : (MAX_SIGNATURE_VALUE_LENGTH - data.length);
+ System.arraycopy(data, maxExceeded ? 1 : 0,
+ result, offset + dstOffset,
+ Math.min(MAX_SIGNATURE_VALUE_LENGTH, data.length));
+ }
+
+ @Override
+ public boolean verify(byte[] sig) throws Exception {
+ int sigLen = NumberUtils.length(sig);
+ byte[] data = sig;
+
+ if (sigLen != DSA_SIGNATURE_LENGTH) {
+ // probably some encoded data
+ Map.Entry<String, byte[]> encoding = extractEncodedSignature(sig);
+ if (encoding != null) {
+ String keyType = encoding.getKey();
+ ValidateUtils.checkTrue(KeyPairProvider.SSH_DSS.equals(keyType), "Mismatched key type: %s", keyType);
+ data = encoding.getValue();
+ sigLen = NumberUtils.length(data);
+ }
+ }
+
+ if (sigLen != DSA_SIGNATURE_LENGTH) {
+ throw new SignatureException("Bad signature length (" + sigLen + " instead of " + DSA_SIGNATURE_LENGTH + ")"
+ + " for " + BufferUtils.toHex(':', data));
+ }
+
+ byte[] rEncoding;
+ try (DERWriter w = new DERWriter(MAX_SIGNATURE_VALUE_LENGTH + 4)) { // in case length > 0x7F
+ w.writeBigInteger(data, 0, MAX_SIGNATURE_VALUE_LENGTH);
+ rEncoding = w.toByteArray();
+ }
+
+ byte[] sEncoding;
+ try (DERWriter w = new DERWriter(MAX_SIGNATURE_VALUE_LENGTH + 4)) { // in case length > 0x7F
+ w.writeBigInteger(data, MAX_SIGNATURE_VALUE_LENGTH, MAX_SIGNATURE_VALUE_LENGTH);
+ sEncoding = w.toByteArray();
+ }
+
+ int length = rEncoding.length + sEncoding.length;
+ byte[] encoded;
+ try (DERWriter w = new DERWriter(1 + length + 4)) { // in case length > 0x7F
+ w.write(0x30); // SEQUENCE
+ w.writeLength(length);
+ w.write(rEncoding);
+ w.write(sEncoding);
+ encoded = w.toByteArray();
+ }
+
+ return doVerify(encoded);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
new file mode 100644
index 0000000..56964d3
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.signature;
+
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.util.Map;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.io.der.DERParser;
+import org.apache.sshd.common.util.io.der.DERWriter;
+
+/**
+ * Signature algorithm for EC keys using ECDSA.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="http://tools.ietf.org/html/rfc3278#section-8.2">RFC3278 section 8.2</A>
+ */
+public class SignatureECDSA extends AbstractSignature {
+ public static class SignatureECDSA256 extends SignatureECDSA {
+ public static final String DEFAULT_ALGORITHM = "SHA256withECDSA";
+
+ public SignatureECDSA256() {
+ super(DEFAULT_ALGORITHM);
+ }
+ }
+
+ public static class SignatureECDSA384 extends SignatureECDSA {
+ public static final String DEFAULT_ALGORITHM = "SHA384withECDSA";
+
+ public SignatureECDSA384() {
+ super(DEFAULT_ALGORITHM);
+ }
+ }
+
+ public static class SignatureECDSA521 extends SignatureECDSA {
+ public static final String DEFAULT_ALGORITHM = "SHA512withECDSA";
+
+ public SignatureECDSA521() {
+ super(DEFAULT_ALGORITHM);
+ }
+ }
+
+ protected SignatureECDSA(String algo) {
+ super(algo);
+ }
+
+ @Override
+ public byte[] sign() throws Exception {
+ byte[] sig = super.sign();
+
+ try (DERParser parser = new DERParser(sig)) {
+ int type = parser.read();
+ if (type != 0x30) {
+ throw new StreamCorruptedException("Invalid signature format - not a DER SEQUENCE: 0x" + Integer.toHexString(type));
+ }
+
+ // length of remaining encoding of the 2 integers
+ int remainLen = parser.readLength();
+ /*
+ * There are supposed to be 2 INTEGERs, each encoded with:
+ *
+ * - one byte representing the fact that it is an INTEGER
+ * - one byte of the integer encoding length
+ * - at least one byte of integer data (zero length is not an option)
+ */
+ if (remainLen < (2 * 3)) {
+ throw new StreamCorruptedException("Invalid signature format - not enough encoded data length: " + remainLen);
+ }
+
+ BigInteger r = parser.readBigInteger();
+ BigInteger s = parser.readBigInteger();
+ // Write the <r,s> to its own types writer.
+ Buffer rsBuf = new ByteArrayBuffer();
+ rsBuf.putMPInt(r);
+ rsBuf.putMPInt(s);
+
+ return rsBuf.getCompactData();
+ }
+ }
+
+ @Override
+ public boolean verify(byte[] sig) throws Exception {
+ byte[] data = sig;
+ Map.Entry<String, byte[]> encoding = extractEncodedSignature(data);
+ if (encoding != null) {
+ String keyType = encoding.getKey();
+ ECCurves curve = ECCurves.fromKeyType(keyType);
+ ValidateUtils.checkNotNull(curve, "Unknown curve type: %s", keyType);
+ data = encoding.getValue();
+ }
+
+ Buffer rsBuf = new ByteArrayBuffer(data);
+ byte[] rArray = rsBuf.getMPIntAsBytes();
+ byte[] rEncoding;
+ try (DERWriter w = new DERWriter(rArray.length + 4)) { // in case length > 0x7F
+ w.writeBigInteger(rArray);
+ rEncoding = w.toByteArray();
+ }
+
+ byte[] sArray = rsBuf.getMPIntAsBytes();
+ byte[] sEncoding;
+ try (DERWriter w = new DERWriter(sArray.length + 4)) { // in case length > 0x7F
+ w.writeBigInteger(sArray);
+ sEncoding = w.toByteArray();
+ }
+
+ int remaining = rsBuf.available();
+ if (remaining != 0) {
+ throw new StreamCorruptedException("Signature had padding - remaining=" + remaining);
+ }
+
+ int length = rEncoding.length + sEncoding.length;
+ byte[] encoded;
+ try (DERWriter w = new DERWriter(1 + length + 4)) { // in case length > 0x7F
+ w.write(0x30); // SEQUENCE
+ w.writeLength(length);
+ w.write(rEncoding);
+ w.write(sEncoding);
+ encoded = w.toByteArray();
+ }
+
+ return doVerify(encoded);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java
new file mode 100644
index 0000000..c9d876a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.signature;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Manage the list of named factories for <code>Signature</code>.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SignatureFactoriesManager {
+ /**
+ * @return The list of named <code>Signature</code> factories
+ */
+ List<NamedFactory<Signature>> getSignatureFactories();
+
+ default String getSignatureFactoriesNameList() {
+ return NamedResource.getNames(getSignatureFactories());
+ }
+
+ default List<String> getSignatureFactoriesNames() {
+ return NamedResource.getNameList(getSignatureFactories());
+ }
+
+ void setSignatureFactories(List<NamedFactory<Signature>> factories);
+
+ default void setSignatureFactoriesNameList(String names) {
+ setSignatureFactoriesNames(GenericUtils.split(names, ','));
+ }
+
+ default void setSignatureFactoriesNames(String... names) {
+ setSignatureFactoriesNames(GenericUtils.isEmpty((Object[]) names) ? Collections.emptyList() : Arrays.asList(names));
+ }
+
+ default void setSignatureFactoriesNames(Collection<String> names) {
+ BuiltinSignatures.ParseResult result = BuiltinSignatures.parseSignatureList(names);
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ List<NamedFactory<Signature>> factories =
+ (List) ValidateUtils.checkNotNullAndNotEmpty(result.getParsedFactories(), "No supported signature factories: %s", names);
+ Collection<String> unsupported = result.getUnsupportedFactories();
+ ValidateUtils.checkTrue(GenericUtils.isEmpty(unsupported), "Unsupported signature factories found: %s", unsupported);
+ setSignatureFactories(factories);
+ }
+
+ /**
+ * Attempts to use the primary manager's signature factories if not {@code null}/empty,
+ * otherwise uses the secondary ones (regardless of whether there are any...)
+ *
+ * @param primary The primary {@link SignatureFactoriesManager}
+ * @param secondary The secondary {@link SignatureFactoriesManager}
+ * @return The resolved signature factories - may be {@code null}/empty
+ * @see #getSignatureFactories(SignatureFactoriesManager)
+ */
+ static List<NamedFactory<Signature>> resolveSignatureFactories(
+ SignatureFactoriesManager primary, SignatureFactoriesManager secondary) {
+ List<NamedFactory<Signature>> factories = getSignatureFactories(primary);
+ return GenericUtils.isEmpty(factories) ? getSignatureFactories(secondary) : factories;
+ }
+
+ /**
+ * @param manager The {@link SignatureFactoriesManager} instance - ignored if {@code null}
+ * @return The associated list of named <code>Signature</code> factories or {@code null} if
+ * no manager instance
+ */
+ static List<NamedFactory<Signature>> getSignatureFactories(SignatureFactoriesManager manager) {
+ return (manager == null) ? null : manager.getSignatureFactories();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
new file mode 100644
index 0000000..0881714
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.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.signature;
+
+import org.apache.sshd.common.BuiltinFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+// CHECKSTYLE:OFF
+public interface SignatureFactory extends BuiltinFactory<Signature> {
+ // 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/signature/SignatureRSA.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
new file mode 100644
index 0000000..ad1d37e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSA.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.signature;
+
+import java.math.BigInteger;
+import java.security.PublicKey;
+import java.security.interfaces.RSAKey;
+import java.util.Map;
+
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * RSA <code>Signature</code>
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-6.6">RFC4253 section 6.6</A>
+ */
+public class SignatureRSA extends AbstractSignature {
+ public static final String DEFAULT_ALGORITHM = "SHA1withRSA";
+
+ private int verifierSignatureSize = -1;
+
+ public SignatureRSA() {
+ super(DEFAULT_ALGORITHM);
+ }
+
+ protected SignatureRSA(String algorithm) {
+ super(algorithm);
+ }
+
+ /**
+ * @return The expected number of bytes in the signature - non-positive
+ * if not initialized or not intended to be used for verification
+ */
+ protected int getVerifierSignatureSize() {
+ return verifierSignatureSize;
+ }
+
+ @Override
+ public void initVerifier(PublicKey key) throws Exception {
+ super.initVerifier(key);
+ RSAKey rsaKey = ValidateUtils.checkInstanceOf(key, RSAKey.class, "Not an RSA key");
+ verifierSignatureSize = getVerifierSignatureSize(rsaKey);
+ }
+
+ public static int getVerifierSignatureSize(RSAKey key) {
+ BigInteger modulus = key.getModulus();
+ return (modulus.bitLength() + Byte.SIZE - 1) / Byte.SIZE;
+ }
+
+ @Override
+ public boolean verify(byte[] sig) throws Exception {
+ byte[] data = sig;
+ Map.Entry<String, byte[]> encoding = extractEncodedSignature(data);
+ if (encoding != null) {
+ String keyType = encoding.getKey();
+ ValidateUtils.checkTrue(KeyPairProvider.SSH_RSA.equals(keyType), "Mismatched key type: %s", keyType);
+ data = encoding.getValue();
+ }
+
+ int expectedSize = getVerifierSignatureSize();
+ ValidateUtils.checkTrue(expectedSize > 0, "Signature verification size has not been initialized");
+ // Pad with zero if value is trimmed
+ if (data.length < expectedSize) {
+ byte[] pad = new byte[expectedSize];
+ System.arraycopy(data, 0, pad, pad.length - data.length, data.length);
+ data = pad;
+ }
+
+ return doVerify(data);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/signature/package.html
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/package.html b/sshd-common/src/main/java/org/apache/sshd/common/signature/package.html
new file mode 100644
index 0000000..d1b16e4
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/package.html
@@ -0,0 +1,25 @@
+<!--
+ 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.
+-->
+<html>
+<head>
+</head>
+<body>
+
+<a href="{@docRoot}/org/apache/sshd/common/signature/Signature.html"><code>Signature</code></a> implementations.
+
+</body>
+</html>
[33/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java
new file mode 100644
index 0000000..0c8f43c
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java
@@ -0,0 +1,325 @@
+/*
+ * 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.client.config.hosts;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class HostConfigEntryTest extends JUnitTestSupport {
+ public HostConfigEntryTest() {
+ super();
+ }
+
+ @Test
+ public void testNegatingPatternOverridesAll() {
+ String testHost = "37.77.34.7";
+ String[] elements = GenericUtils.split(testHost, '.');
+ StringBuilder sb = new StringBuilder(testHost.length() + Byte.SIZE);
+ List<HostPatternValue> patterns = new ArrayList<>(elements.length + 1);
+ // all wildcard patterns are not negated - only the actual host
+ patterns.add(HostPatternsHolder.toPattern(Character.toString(HostPatternsHolder.NEGATION_CHAR_PATTERN) + testHost));
+
+ for (int i = 0; i < elements.length; i++) {
+ sb.setLength(0);
+
+ for (int j = 0; j < elements.length; j++) {
+ if (j > 0) {
+ sb.append('.');
+ }
+ if (i == j) {
+ sb.append(HostPatternsHolder.WILDCARD_PATTERN);
+ } else {
+ sb.append(elements[j]);
+ }
+ }
+
+ patterns.add(HostPatternsHolder.toPattern(sb));
+ }
+
+ for (int index = 0; index < patterns.size(); index++) {
+ assertFalse("Unexpected match for " + patterns, HostPatternsHolder.isHostMatch(testHost, 0, patterns));
+ Collections.shuffle(patterns);
+ }
+ }
+
+ @Test
+ public void testHostWildcardPatternMatching() {
+ String pkgName = getClass().getPackage().getName();
+ String[] elements = GenericUtils.split(pkgName, '.');
+ StringBuilder sb = new StringBuilder(pkgName.length() + Long.SIZE + 1).append(HostPatternsHolder.WILDCARD_PATTERN);
+ for (int index = elements.length - 1; index >= 0; index--) {
+ sb.append('.').append(elements[index]);
+ }
+
+ String value = sb.toString();
+ HostPatternValue pp = HostPatternsHolder.toPattern(value);
+ Pattern pattern = pp.getPattern();
+ String domain = value.substring(1); // chomp the wildcard prefix
+ for (String host : new String[] {
+ getClass().getSimpleName(),
+ getCurrentTestName(),
+ getClass().getSimpleName() + "-" + getCurrentTestName(),
+ getClass().getSimpleName() + "." + getCurrentTestName(),
+ }) {
+ sb.setLength(0); // start from scratch
+ sb.append(host).append(domain);
+
+ testCaseInsensitivePatternMatching(sb.toString(), pattern, true);
+ }
+ }
+
+ @Test
+ public void testIPAddressWildcardPatternMatching() {
+ StringBuilder sb = new StringBuilder().append("10.0.0.");
+ int sbLen = sb.length();
+
+ Pattern pattern = HostPatternsHolder.toPattern(sb.append(HostPatternsHolder.WILDCARD_PATTERN)).getPattern();
+ for (int v = 0; v <= 255; v++) {
+ sb.setLength(sbLen); // start from scratch
+ sb.append(v);
+
+ String address = sb.toString();
+ assertTrue("No match for " + address, HostPatternsHolder.isHostMatch(address, pattern));
+ }
+ }
+
+ @Test
+ public void testHostSingleCharPatternMatching() {
+ String value = getCurrentTestName();
+ StringBuilder sb = new StringBuilder(value);
+ for (boolean restoreOriginal : new boolean[] {true, false}) {
+ for (int index = 0; index < value.length(); index++) {
+ sb.setCharAt(index, HostPatternsHolder.SINGLE_CHAR_PATTERN);
+ testCaseInsensitivePatternMatching(value, HostPatternsHolder.toPattern(sb.toString()).getPattern(), true);
+ if (restoreOriginal) {
+ sb.setCharAt(index, value.charAt(index));
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testIPAddressSingleCharPatternMatching() {
+ StringBuilder sb = new StringBuilder().append("10.0.0.");
+ int sbLen = sb.length();
+
+ for (int v = 0; v <= 255; v++) {
+ sb.setLength(sbLen); // start from scratch
+ sb.append(v);
+
+ String address = sb.toString();
+ // replace the added digits with single char pattern
+ for (int index = sbLen; index < sb.length(); index++) {
+ sb.setCharAt(index, HostPatternsHolder.SINGLE_CHAR_PATTERN);
+ }
+
+ String pattern = sb.toString();
+ HostPatternValue pp = HostPatternsHolder.toPattern(pattern);
+ assertTrue("No match for " + address + " on pattern=" + pattern, HostPatternsHolder.isHostMatch(address, 0, Collections.singletonList(pp)));
+ }
+ }
+
+ @Test
+ public void testIsValidPatternChar() {
+ for (char ch = '\0'; ch <= ' '; ch++) {
+ assertFalse("Unexpected valid character (0x" + Integer.toHexString(ch & 0xFF) + ")", HostPatternsHolder.isValidPatternChar(ch));
+ }
+
+ for (char ch = 'a'; ch <= 'z'; ch++) {
+ assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
+ }
+
+ for (char ch = 'A'; ch <= 'Z'; ch++) {
+ assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
+ }
+
+ for (char ch = '0'; ch <= '9'; ch++) {
+ assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
+ }
+
+ for (char ch : new char[] {'-', '_', '.', HostPatternsHolder.SINGLE_CHAR_PATTERN, HostPatternsHolder.WILDCARD_PATTERN}) {
+ assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
+ }
+
+ for (char ch : new char[] {
+ '(', ')', '{', '}', '[', ']', '@',
+ '#', '$', '^', '&', '%', '~', '<', '>',
+ ',', '/', '\\', '\'', '"', ':', ';'
+ }) {
+ assertFalse("Unexpected valid character: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
+ }
+
+ for (char ch = 0x7E; ch <= 0xFF; ch++) {
+ assertFalse("Unexpected valid character (0x" + Integer.toHexString(ch & 0xFF) + ")", HostPatternsHolder.isValidPatternChar(ch));
+ }
+ }
+
+ @Test
+ public void testResolvePort() {
+ final int originalPort = Short.MAX_VALUE;
+ final int preferredPort = 7365;
+ assertEquals("Mismatched entry port preference",
+ preferredPort, HostConfigEntry.resolvePort(originalPort, preferredPort));
+
+ for (int entryPort : new int[] {-1, 0}) {
+ assertEquals("Non-preferred original port for entry port=" + entryPort,
+ originalPort, HostConfigEntry.resolvePort(originalPort, entryPort));
+ }
+ }
+
+ @Test
+ public void testResolveUsername() {
+ final String originalUser = getCurrentTestName();
+ final String preferredUser = getClass().getSimpleName();
+ assertSame("Mismatched entry user preference",
+ preferredUser, HostConfigEntry.resolveUsername(originalUser, preferredUser));
+
+ for (String entryUser : new String[] {null, ""}) {
+ assertSame("Non-preferred original user for entry user='" + entryUser + "'",
+ originalUser, HostConfigEntry.resolveUsername(originalUser, entryUser));
+ }
+ }
+
+ @Test
+ public void testReadSimpleHostsConfigEntries() throws IOException {
+ validateHostConfigEntries(readHostConfigEntries());
+ }
+
+ @Test
+ public void testReadGlobalHostsConfigEntries() throws IOException {
+ List<HostConfigEntry> entries = validateHostConfigEntries(readHostConfigEntries());
+ assertTrue("Not enough entries read", GenericUtils.size(entries) > 1);
+
+ // global entry MUST be 1st one
+ HostConfigEntry globalEntry = entries.get(0);
+ assertEquals("Mismatched global entry pattern", HostPatternsHolder.ALL_HOSTS_PATTERN, globalEntry.getHost());
+
+ for (int index = 1; index < entries.size(); index++) {
+ HostConfigEntry entry = entries.get(index);
+ assertFalse("No target host for " + entry, GenericUtils.isEmpty(entry.getHostName()));
+ assertTrue("No target port for " + entry, entry.getPort() > 0);
+ assertFalse("No username for " + entry, GenericUtils.isEmpty(entry.getUsername()));
+ assertFalse("No identities for " + entry, GenericUtils.isEmpty(entry.getIdentities()));
+ assertFalse("No properties for " + entry, GenericUtils.isEmpty(entry.getProperties()));
+ }
+ }
+
+ @Test
+ public void testReadMultipleHostPatterns() throws IOException {
+ List<HostConfigEntry> entries = validateHostConfigEntries(readHostConfigEntries());
+ assertEquals("Mismatched number of entries", 1, GenericUtils.size(entries));
+ assertEquals("Mismatched number of patterns", 3, GenericUtils.size(entries.get(0).getPatterns()));
+ }
+
+ @Test
+ public void testResolveIdentityFilePath() throws Exception {
+ final String hostValue = getClass().getSimpleName();
+ final int portValue = 7365;
+ final String userValue = getCurrentTestName();
+
+ Exception err = null;
+ for (String pattern : new String[] {
+ "~/.ssh/%h.key",
+ "%d/.ssh/%h.key",
+ "/home/%u/.ssh/id_rsa_%p",
+ "/home/%u/.ssh/id_%r_rsa",
+ "/home/%u/.ssh/%h/%l.key"
+ }) {
+ try {
+ String result = HostConfigEntry.resolveIdentityFilePath(pattern, hostValue, portValue, userValue);
+ System.out.append('\t').append(pattern).append(" => ").println(result);
+ } catch (Exception e) {
+ System.err.append("Failed (").append(e.getClass().getSimpleName())
+ .append(") to process pattern=").append(pattern)
+ .append(": ").println(e.getMessage());
+ err = e;
+ }
+ }
+
+ if (err != null) {
+ throw err;
+ }
+ }
+
+ @Test
+ public void testFindBestMatch() {
+ final String hostValue = getCurrentTestName();
+ HostConfigEntry expected = new HostConfigEntry(hostValue, hostValue, 7365, hostValue);
+ List<HostConfigEntry> matches = new ArrayList<>();
+ matches.add(new HostConfigEntry(HostPatternsHolder.ALL_HOSTS_PATTERN,
+ getClass().getSimpleName(), Short.MAX_VALUE, getClass().getSimpleName()));
+ matches.add(new HostConfigEntry(hostValue + Character.toString(HostPatternsHolder.WILDCARD_PATTERN),
+ getClass().getSimpleName(), Byte.MAX_VALUE, getClass().getSimpleName()));
+ matches.add(expected);
+
+ for (int index = 0; index < matches.size(); index++) {
+ HostConfigEntry actual = HostConfigEntry.findBestMatch(matches);
+ assertSame("Mismatched best match for " + matches, expected, actual);
+ Collections.shuffle(matches);
+ }
+ }
+
+ private static <C extends Collection<HostConfigEntry>> C validateHostConfigEntries(C entries) {
+ assertFalse("No entries", GenericUtils.isEmpty(entries));
+
+ for (HostConfigEntry entry : entries) {
+ assertFalse("No pattern for " + entry, GenericUtils.isEmpty(entry.getHost()));
+ assertFalse("No extra properties for " + entry, GenericUtils.isEmpty(entry.getProperties()));
+ }
+
+ return entries;
+ }
+
+ private List<HostConfigEntry> readHostConfigEntries() throws IOException {
+ return readHostConfigEntries(getCurrentTestName() + ".config.txt");
+ }
+
+ private List<HostConfigEntry> readHostConfigEntries(String resourceName) throws IOException {
+ URL url = getClass().getResource(resourceName);
+ assertNotNull("Missing resource " + resourceName, url);
+ return HostConfigEntry.readHostConfigEntries(url);
+ }
+
+ private static void testCaseInsensitivePatternMatching(String value, Pattern pattern, boolean expected) {
+ for (int index = 0; index < value.length(); index++) {
+ boolean actual = HostPatternsHolder.isHostMatch(value, pattern);
+ assertEquals("Mismatched match result for " + value + " on pattern=" + pattern.pattern(), expected, actual);
+ value = shuffleCase(value);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
new file mode 100644
index 0000000..80d58d0
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.client.config.hosts;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class KnownHostHashValueTest extends JUnitTestSupport {
+ private final String hostName;
+ private final String hashValue;
+ private final KnownHostHashValue hash;
+
+ public KnownHostHashValueTest(String hostName, String hashValue) {
+ this.hostName = hostName;
+ this.hashValue = hashValue;
+ this.hash = KnownHostHashValue.parse(hashValue);
+ }
+
+ @Parameters(name = "host={0}, hash={1}")
+ public static Collection<Object[]> parameters() {
+ return Arrays.<Object[]>asList(
+ (Object[]) new String[]{"192.168.1.61", "|1|F1E1KeoE/eEWhi10WpGv4OdiO6Y=|3988QV0VE8wmZL7suNrYQLITLCg="});
+ }
+
+ @Test
+ public void testDecodeEncode() {
+ assertSame("Mismatched digester", KnownHostHashValue.DEFAULT_DIGEST, hash.getDigester());
+ assertEquals("Mismatched encoded form", hashValue, hash.toString());
+ }
+
+ @Test
+ public void testHostMatch() {
+ assertTrue("Specified host does not match", hash.isHostMatch(hostName));
+ assertFalse("Unexpected host match", hash.isHostMatch(getCurrentTestName()));
+ }
+
+ @Test
+ public void testCalculateHashValue() throws Exception {
+ byte[] expected = hash.getDigestValue();
+ byte[] actual = KnownHostHashValue.calculateHashValue(hostName, hash.getDigester(), hash.getSaltValue());
+ assertArrayEquals("Mismatched hash value", expected, actual);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
new file mode 100644
index 0000000..19375ec
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.client.config.keys;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.BuiltinIdentities;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+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.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BuiltinClientIdentitiesWatcherTest extends JUnitTestSupport {
+ public BuiltinClientIdentitiesWatcherTest() {
+ super();
+ }
+
+ @Test
+ public void testMultipleFilesWatch() throws Exception {
+ KeyPair identity = CommonTestSupportUtils.getFirstKeyPair(createTestHostKeyProvider());
+ String keyType = ValidateUtils.checkNotNullAndNotEmpty(KeyUtils.getKeyType(identity), "Cannot determine identity key type");
+
+ Path dir = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
+ Map<BuiltinIdentities, Path> locationsMap = new EnumMap<>(BuiltinIdentities.class);
+ Map<BuiltinIdentities, KeyPair> idsMap = new EnumMap<>(BuiltinIdentities.class);
+ for (BuiltinIdentities id : BuiltinIdentities.VALUES) {
+ Path idFile = dir.resolve(ClientIdentity.getIdentityFileName(id));
+ Files.deleteIfExists(idFile);
+ assertNull("Multiple file mappings for " + id, locationsMap.put(id, idFile));
+ assertNull("Multiple identity mappings for " + id, idsMap.put(id, KeyUtils.cloneKeyPair(keyType, identity)));
+ }
+
+ ClientIdentityLoader loader = new ClientIdentityLoader() {
+ @Override
+ public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
+ BuiltinIdentities id = findIdentity(location);
+ assertNotNull("Invalid location: " + location, id);
+ return idsMap.get(id);
+ }
+
+ @Override
+ public boolean isValidLocation(String location) throws IOException {
+ return findIdentity(location) != null;
+ }
+
+ private BuiltinIdentities findIdentity(String location) {
+ if (GenericUtils.isEmpty(location)) {
+ return null;
+ }
+
+ for (Map.Entry<BuiltinIdentities, Path> le : locationsMap.entrySet()) {
+ Path path = le.getValue();
+ if (String.CASE_INSENSITIVE_ORDER.compare(location, path.toString()) == 0) {
+ return le.getKey();
+ }
+ }
+
+ return null;
+ }
+ };
+
+ Map<BuiltinIdentities, KeyPair> existing = new EnumMap<>(BuiltinIdentities.class);
+ KeyPairProvider watcher = new BuiltinClientIdentitiesWatcher(dir, false, loader, FilePasswordProvider.EMPTY, false);
+ testMultipleFilesWatch("No files", watcher, existing.values());
+
+ for (BuiltinIdentities id : BuiltinIdentities.VALUES) {
+ String phase = id + " + " + Objects.toString(existing.keySet());
+ touchIdentityFile(locationsMap.get(id));
+ existing.put(id, idsMap.get(id));
+
+ for (int index = 0; index < Byte.SIZE; index++) {
+ testMultipleFilesWatch(phase + "[" + index + "]", watcher, existing.values());
+ }
+ }
+
+ testMultipleFilesWatch("All files", watcher, existing.values());
+
+ for (BuiltinIdentities id : BuiltinIdentities.VALUES) {
+ existing.remove(id);
+ Files.deleteIfExists(locationsMap.get(id));
+ String phase = Objects.toString(existing.keySet()) + " - " + id;
+
+ for (int index = 0; index < Byte.SIZE; index++) {
+ testMultipleFilesWatch(phase + "[" + index + "]", watcher, existing.values());
+ }
+ }
+ }
+
+ private static void touchIdentityFile(Path idFile) throws IOException {
+ OpenOption[] options = IoUtils.EMPTY_OPEN_OPTIONS;
+ if (Files.exists(idFile, IoUtils.EMPTY_LINK_OPTIONS)) {
+ options = new OpenOption[]{StandardOpenOption.WRITE, StandardOpenOption.APPEND};
+ }
+
+ try (OutputStream out = Files.newOutputStream(idFile, options)) {
+ out.write(new Date(System.currentTimeMillis()).toString().getBytes(StandardCharsets.UTF_8));
+ out.write('\n');
+ }
+ }
+
+ private static void testMultipleFilesWatch(String phase, KeyIdentityProvider watcher, Collection<? extends KeyPair> expected) {
+ Iterable<KeyPair> keys = watcher.loadKeys();
+ Collection<KeyPair> actual = new ArrayList<>();
+ for (KeyPair kp : keys) {
+ actual.add(kp);
+ }
+ assertEquals(phase + ": mismatched sizes", GenericUtils.size(expected), GenericUtils.size(actual));
+
+ if (!GenericUtils.isEmpty(expected)) {
+ for (KeyPair kp : expected) {
+ assertTrue(phase + ": missing key", actual.contains(kp));
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
new file mode 100644
index 0000000..144a3b0
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.client.config.keys;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.Date;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class ClientIdentityFileWatcherTest extends JUnitTestSupport {
+ public ClientIdentityFileWatcherTest() {
+ super();
+ }
+
+ @Test
+ public void testIdentityReload() throws Exception {
+ Path dir = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
+ Path idFile = dir.resolve(getCurrentTestName() + ".pem");
+ KeyPair identity = CommonTestSupportUtils.getFirstKeyPair(createTestHostKeyProvider());
+ ClientIdentityLoader loader = new ClientIdentityLoader() {
+ @Override
+ public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
+ assertTrue("Invalid location: " + location, isValidLocation(location));
+ return identity;
+ }
+
+ @Override
+ public boolean isValidLocation(String location) throws IOException {
+ return Objects.equals(location, toString());
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toString(idFile);
+ }
+ };
+
+ AtomicInteger reloadCount = new AtomicInteger(0);
+ ClientIdentityProvider idProvider = new ClientIdentityFileWatcher(idFile, loader, FilePasswordProvider.EMPTY, false) {
+ @Override
+ protected KeyPair reloadClientIdentity(Path path) throws IOException, GeneralSecurityException {
+ assertEquals("Mismatched client identity path", idFile, path);
+ reloadCount.incrementAndGet();
+ return super.reloadClientIdentity(path);
+ }
+ };
+ Files.deleteIfExists(idFile);
+
+ testIdentityReload("Non-existing", reloadCount, idProvider, null, 0);
+
+ touchIdentityFile(idFile);
+ for (int index = 1; index < Byte.SIZE; index++) {
+ testIdentityReload("Created iteration " + 1, reloadCount, idProvider, identity, 1);
+ }
+
+ touchIdentityFile(idFile);
+ for (int index = 1; index < Byte.SIZE; index++) {
+ testIdentityReload("Modified iteration " + 1, reloadCount, idProvider, identity, 2);
+ }
+ }
+
+ private static void touchIdentityFile(Path idFile) throws IOException {
+ OpenOption[] options = IoUtils.EMPTY_OPEN_OPTIONS;
+ if (Files.exists(idFile, IoUtils.EMPTY_LINK_OPTIONS)) {
+ options = new OpenOption[]{StandardOpenOption.WRITE, StandardOpenOption.APPEND};
+ }
+
+ try (OutputStream out = Files.newOutputStream(idFile, options)) {
+ out.write(new Date(System.currentTimeMillis()).toString().getBytes(StandardCharsets.UTF_8));
+ out.write('\n');
+ }
+ }
+
+ private static void testIdentityReload(
+ String phase, Number reloadCount, ClientIdentityProvider provider, KeyPair expectedIdentity, int expectedCount)
+ throws Exception {
+ KeyPair actualIdentity = provider.getClientIdentity();
+ assertSame(phase + ": mismatched identity", expectedIdentity, actualIdentity);
+ assertEquals(phase + ": mismatched re-load count", expectedCount, reloadCount.intValue());
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
new file mode 100644
index 0000000..bc2f02c
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.client.config.keys;
+
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.Map;
+
+import org.apache.sshd.common.config.keys.BuiltinIdentities;
+import org.apache.sshd.common.config.keys.IdentityUtils;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class ClientIdentityTest extends JUnitTestSupport {
+ public ClientIdentityTest() {
+ super();
+ }
+
+ @Test
+ public void testLoadClientIdentities() throws Exception {
+ Path resFolder = getTestResourcesFolder();
+ LinkOption[] options = IoUtils.getLinkOptions(true);
+ Collection<BuiltinIdentities> expected = EnumSet.noneOf(BuiltinIdentities.class);
+ for (BuiltinIdentities type : BuiltinIdentities.VALUES) {
+ String fileName = ClientIdentity.getIdentityFileName(type);
+ Path file = resFolder.resolve(fileName);
+ if (!Files.exists(file, options)) {
+ System.out.println("Skip non-existing identity file " + file);
+ continue;
+ }
+
+ if (!type.isSupported()) {
+ System.out.println("Skip unsupported identity file " + file);
+ continue;
+ }
+
+ expected.add(type);
+ }
+
+ Map<String, KeyPair> ids = ClientIdentity.loadDefaultIdentities(
+ resFolder,
+ false, // don't be strict
+ null, // none of the files is password protected
+ options);
+ assertEquals("Mismatched loaded ids count", GenericUtils.size(expected), GenericUtils.size(ids));
+
+ Collection<KeyPair> pairs = new ArrayList<>(ids.size());
+ for (BuiltinIdentities type : BuiltinIdentities.VALUES) {
+ if (expected.contains(type)) {
+ KeyPair kp = ids.get(type.getName());
+ assertNotNull("No key pair loaded for " + type, kp);
+ pairs.add(kp);
+ }
+ }
+
+ KeyIdentityProvider provider = IdentityUtils.createKeyPairProvider(ids, true /* supported only */);
+ assertNotNull("No provider generated", provider);
+
+ Iterable<KeyPair> keys = provider.loadKeys();
+ for (KeyPair kp : keys) {
+ assertTrue("Unexpected loaded key: " + kp, pairs.remove(kp));
+ }
+
+ assertEquals("Not all pairs listed", 0, pairs.size());
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/SshConstantsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/SshConstantsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/SshConstantsTest.java
new file mode 100644
index 0000000..1d123d3
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/SshConstantsTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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 org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class SshConstantsTest extends JUnitTestSupport {
+ public SshConstantsTest() {
+ super();
+ }
+
+ @Test
+ public void testGetDisconnectReason() {
+ for (int reason = SshConstants.SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT; reason <= SshConstants.SSH2_DISCONNECT_ILLEGAL_USER_NAME; reason++) {
+ String name = SshConstants.getDisconnectReasonName(reason);
+ assertTrue("Mismatched name for reason=" + reason + ": " + name, name.startsWith("SSH2_DISCONNECT_"));
+ }
+ }
+
+ @Test
+ public void testGetOpenErrorName() {
+ for (int code = SshConstants.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; code <= SshConstants.SSH_OPEN_RESOURCE_SHORTAGE; code++) {
+ String name = SshConstants.getOpenErrorCodeName(code);
+ assertTrue("Mismatched name for code=" + code + ": " + name, name.startsWith("SSH_OPEN_"));
+ }
+ }
+
+ @Test
+ public void testAmbiguousOpcodes() throws Exception {
+ int[] knownAmbiguities = {30, 31, 60};
+ Collection<Integer> opcodes = SshConstants.getAmbiguousOpcodes();
+ assertTrue("Not enough ambiguities found", GenericUtils.size(opcodes) >= knownAmbiguities.length);
+
+ for (int cmd : knownAmbiguities) {
+ assertEquals("Mismatched mnemonic for known ambiguity=" + cmd, Integer.toString(cmd), SshConstants.getCommandMessageName(cmd));
+ assertTrue("Known ambiguity not reported as such: " + cmd, SshConstants.isAmbiguousOpcode(cmd));
+ assertTrue("Known ambiguity=" + cmd + " not listed: " + opcodes, opcodes.contains(cmd));
+ }
+
+ for (Integer cmd : opcodes) {
+ assertEquals("Mismatched mnemonic for " + cmd, cmd.toString(), SshConstants.getCommandMessageName(cmd));
+ assertTrue("Opcode not detected as ambiguous: " + cmd, SshConstants.isAmbiguousOpcode(cmd));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java b/sshd-common/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java
new file mode 100644
index 0000000..c0978d4
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/VersionPropertiesTest.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;
+
+import java.util.Map;
+
+import org.apache.sshd.common.config.VersionProperties;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class VersionPropertiesTest extends JUnitTestSupport {
+ public VersionPropertiesTest() {
+ super();
+ }
+
+ @Test
+ public void testNonEmptyProperties() {
+ Map<?, ?> props = VersionProperties.getVersionProperties();
+ assertTrue(GenericUtils.isNotEmpty(props));
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java
new file mode 100644
index 0000000..6611702
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AES192CTRTest extends BaseCipherTest {
+ public AES192CTRTest() {
+ super();
+ }
+
+ @Test
+ public void testEncryptDecrypt() throws Exception {
+ // for AES 256 bits we need the JCE unlimited strength policy
+ ensureKeySizeSupported(16, 24, "AES", "AES/CTR/NoPadding");
+ testEncryptDecrypt(BuiltinCiphers.aes192ctr);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java
new file mode 100644
index 0000000..dd39fd4
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AES256CBCTest extends BaseCipherTest {
+ public AES256CBCTest() {
+ super();
+ }
+
+ @Test
+ public void testEncryptDecrypt() throws Exception {
+ // for AES 256 bits we need the JCE unlimited strength policy
+ ensureKeySizeSupported(16, 32, "AES", "AES/CBC/NoPadding");
+ testEncryptDecrypt(BuiltinCiphers.aes256cbc);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java
new file mode 100644
index 0000000..1c37449
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java
@@ -0,0 +1,39 @@
+/*
+ * 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.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class ARCFOUR128Test extends BaseCipherTest {
+ public ARCFOUR128Test() {
+ super();
+ }
+
+ @Test
+ public void testEncryptDecrypt() throws Exception {
+ testEncryptDecrypt(BuiltinCiphers.arcfour128);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java
new file mode 100644
index 0000000..5511e0f
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java
@@ -0,0 +1,41 @@
+/*
+ * 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.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class ARCFOUR256Test extends BaseCipherTest {
+ public ARCFOUR256Test() {
+ super();
+ }
+
+ @Test
+ public void testEncryptDecrypt() throws Exception {
+ // for RC4 256 bits we need the JCE unlimited strength policy
+ ensureKeySizeSupported(32, "ARCFOUR", "RC4");
+ testEncryptDecrypt(BuiltinCiphers.arcfour256);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java
new file mode 100644
index 0000000..514cba4
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.cipher.Cipher.Mode;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.experimental.categories.Category;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@Category({ NoIoTestCase.class })
+public abstract class BaseCipherTest extends JUnitTestSupport {
+ protected BaseCipherTest() {
+ super();
+ }
+
+ protected void ensureKeySizeSupported(int bsize, String algorithm, String transformation) throws GeneralSecurityException {
+ try {
+ javax.crypto.Cipher cipher = SecurityUtils.getCipher(transformation);
+ byte[] key = new byte[bsize];
+ cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, new SecretKeySpec(key, algorithm));
+ } catch (GeneralSecurityException e) {
+ if (e instanceof InvalidKeyException) { // NOTE: assumption violations are NOT test failures...
+ Assume.assumeTrue(algorithm + "/" + transformation + "[" + bsize + "] N/A", false);
+ }
+
+ throw e;
+ }
+ }
+
+ protected void ensureKeySizeSupported(int ivsize, int bsize, String algorithm, String transformation) throws GeneralSecurityException {
+ try {
+ javax.crypto.Cipher cipher = SecurityUtils.getCipher(transformation);
+ byte[] key = new byte[bsize];
+ byte[] iv = new byte[ivsize];
+ cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, new SecretKeySpec(key, algorithm), new IvParameterSpec(iv));
+ } catch (GeneralSecurityException e) {
+ if (e instanceof InvalidKeyException) {
+ Assume.assumeTrue(algorithm + "/" + transformation + "[" + bsize + "/" + ivsize + "]", false /* force exception */);
+ }
+
+ throw e;
+ }
+ }
+
+ protected void testEncryptDecrypt(NamedFactory<Cipher> factory) throws Exception {
+ String facName = factory.getName();
+ Cipher enc = factory.create();
+ int keySize = enc.getBlockSize();
+ int ivSize = enc.getIVSize();
+ byte[] key = new byte[keySize];
+ byte[] iv = new byte[ivSize];
+ enc.init(Mode.Encrypt, key, iv);
+
+ byte[] expected = facName.getBytes(StandardCharsets.UTF_8);
+ byte[] workBuf = expected.clone(); // need to clone since the cipher works in-line
+ enc.update(workBuf, 0, workBuf.length);
+
+ Cipher dec = factory.create();
+ dec.init(Mode.Decrypt, key, iv);
+ byte[] actual = workBuf.clone();
+ dec.update(actual, 0, actual.length);
+
+ assertArrayEquals(facName, expected, actual);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java
new file mode 100644
index 0000000..2294606
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class ECCurvesTest extends JUnitTestSupport {
+ public ECCurvesTest() {
+ super();
+ }
+
+ @Test
+ public void testFromName() {
+ for (ECCurves expected : ECCurves.VALUES) {
+ String name = expected.getName();
+ for (int index = 0; index < name.length(); index++) {
+ ECCurves actual = ECCurves.fromCurveName(name);
+ assertSame(name, expected, actual);
+ name = shuffleCase(name);
+ }
+ }
+ }
+
+ @Test
+ public void testAllNamesListed() {
+ Set<ECCurves> listed = EnumSet.noneOf(ECCurves.class);
+ for (String name : ECCurves.NAMES) {
+ ECCurves c = ECCurves.fromCurveName(name);
+ assertNotNull("No curve for listed name=" + name, c);
+ assertTrue("Duplicated listed name: " + name, listed.add(c));
+ }
+
+ assertEquals("Mismatched listed vs. values", ECCurves.VALUES, listed);
+ }
+
+ @Test
+ public void testFromKeySize() {
+ for (ECCurves expected : ECCurves.VALUES) {
+ String name = expected.getName();
+ ECCurves actual = ECCurves.fromCurveSize(expected.getKeySize());
+ assertSame(name, expected, actual);
+ }
+ }
+
+ @Test
+ public void testFromCurveParameters() {
+ for (ECCurves expected : ECCurves.VALUES) {
+ String name = expected.getName();
+ ECCurves actual = ECCurves.fromCurveParameters(expected.getParameters());
+ assertSame(name, expected, actual);
+ }
+ }
+
+ @Test
+ public void testFromKeyType() {
+ for (ECCurves expected : ECCurves.VALUES) {
+ String keyType = expected.getKeyType();
+ for (int index = 0; index < keyType.length(); index++) {
+ ECCurves actual = ECCurves.fromKeyType(keyType);
+ assertSame(keyType, expected, actual);
+ keyType = shuffleCase(keyType);
+ }
+ }
+ }
+
+ @Test
+ public void testAllKeyTypesListed() {
+ Set<ECCurves> listed = EnumSet.noneOf(ECCurves.class);
+ for (String name : ECCurves.KEY_TYPES) {
+ ECCurves c = ECCurves.fromKeyType(name);
+ assertNotNull("No curve for listed key type=" + name, c);
+ assertTrue("Duplicated listed key type: " + name, listed.add(c));
+ }
+
+ assertEquals("Mismatched listed vs. values", ECCurves.VALUES, listed);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
new file mode 100644
index 0000000..3d1ed00
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.compression;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.compression.BuiltinCompressions.ParseResult;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+import org.mockito.Mockito;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BuiltinCompressionsTest extends JUnitTestSupport {
+ public BuiltinCompressionsTest() {
+ super();
+ }
+
+ @Test
+ public void testFromFactoryName() {
+ for (BuiltinCompressions expected : BuiltinCompressions.VALUES) {
+ String name = expected.getName();
+
+ for (int index = 0; index < name.length(); index++) {
+ BuiltinCompressions actual = BuiltinCompressions.fromFactoryName(name);
+ assertSame(name, expected, actual);
+ name = shuffleCase(name);
+ }
+ }
+ }
+
+ @Test
+ public void testAllConstantsCovered() throws Exception {
+ Set<BuiltinCompressions> avail = EnumSet.noneOf(BuiltinCompressions.class);
+ Field[] fields = BuiltinCompressions.Constants.class.getFields();
+ for (Field f : fields) {
+ String name = (String) f.get(null);
+ BuiltinCompressions value = BuiltinCompressions.fromFactoryName(name);
+ assertNotNull("No match found for " + name, value);
+ assertTrue(name + " re-specified", avail.add(value));
+ }
+
+ assertEquals("Incomplete coverage", BuiltinCompressions.VALUES, avail);
+ }
+
+ @Test
+ public void testParseCompressionsList() {
+ List<String> builtin = NamedResource.getNameList(BuiltinCompressions.VALUES);
+ List<String> unknown = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
+ Random rnd = new Random();
+ for (int index = 0; index < (builtin.size() + unknown.size()); index++) {
+ Collections.shuffle(builtin, rnd);
+ Collections.shuffle(unknown, rnd);
+
+ List<String> weavedList = new ArrayList<>(builtin.size() + unknown.size());
+ for (int bIndex = 0, uIndex = 0; (bIndex < builtin.size()) || (uIndex < unknown.size());) {
+ boolean useBuiltin = false;
+ if (bIndex < builtin.size()) {
+ useBuiltin = uIndex >= unknown.size() || rnd.nextBoolean();
+ }
+
+ if (useBuiltin) {
+ weavedList.add(builtin.get(bIndex));
+ bIndex++;
+ } else if (uIndex < unknown.size()) {
+ weavedList.add(unknown.get(uIndex));
+ uIndex++;
+ }
+ }
+
+ String fullList = GenericUtils.join(weavedList, ',');
+ ParseResult result = BuiltinCompressions.parseCompressionsList(fullList);
+ List<String> parsed = NamedResource.getNameList(result.getParsedFactories());
+ List<String> missing = result.getUnsupportedFactories();
+
+ // makes sure not only that the contents are the same but also the order
+ assertListEquals(fullList + "[parsed]", builtin, parsed);
+ assertListEquals(fullList + "[unsupported]", unknown, missing);
+ }
+ }
+
+ @Test
+ public void testResolveFactoryOnBuiltinValues() {
+ for (NamedFactory<Compression> expected : BuiltinCompressions.VALUES) {
+ String name = expected.getName();
+ NamedFactory<Compression> actual = BuiltinCompressions.resolveFactory(name);
+ assertSame(name, expected, actual);
+ }
+ }
+
+ @Test
+ public void testNotAllowedToRegisterBuiltinFactories() {
+ for (CompressionFactory expected : BuiltinCompressions.VALUES) {
+ try {
+ BuiltinCompressions.registerExtension(expected);
+ fail("Unexpected success for " + expected.getName());
+ } catch (IllegalArgumentException e) {
+ // expected - ignored
+ }
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNotAllowedToOverrideRegisteredFactories() {
+ CompressionFactory expected = Mockito.mock(CompressionFactory.class);
+ Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+ String name = expected.getName();
+ try {
+ for (int index = 1; index <= Byte.SIZE; index++) {
+ BuiltinCompressions.registerExtension(expected);
+ assertEquals("Unexpected success at attempt #" + index, 1, index);
+ }
+ } finally {
+ BuiltinCompressions.unregisterExtension(name);
+ }
+ }
+
+ @Test
+ public void testResolveFactoryOnRegisteredExtension() {
+ CompressionFactory expected = Mockito.mock(CompressionFactory.class);
+ Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+ String name = expected.getName();
+ try {
+ assertNull("Extension already registered", BuiltinCompressions.resolveFactory(name));
+ BuiltinCompressions.registerExtension(expected);
+
+ NamedFactory<Compression> actual = BuiltinCompressions.resolveFactory(name);
+ assertSame("Mismatched resolved instance", expected, actual);
+ } finally {
+ NamedFactory<Compression> actual = BuiltinCompressions.unregisterExtension(name);
+ assertSame("Mismatched unregistered instance", expected, actual);
+ assertNull("Extension not un-registered", BuiltinCompressions.resolveFactory(name));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java
new file mode 100644
index 0000000..0466d95
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.concurrent.TimeUnit;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class TimeValueConfigTest extends JUnitTestSupport {
+ public TimeValueConfigTest() {
+ super();
+ }
+
+ @Test
+ public void testDurationOf() {
+ Object[] values = {
+ "600", TimeUnit.SECONDS.toMillis(600L),
+ "10m", TimeUnit.MINUTES.toMillis(10L),
+ "1h30m", TimeUnit.MINUTES.toMillis(90L),
+ "2d", TimeUnit.DAYS.toMillis(2L),
+ "3w", TimeUnit.DAYS.toMillis(3L * 7L)
+ };
+ for (int index = 0; index < values.length; index += 2) {
+ String s = (String) values[index];
+ Number expected = (Number) values[index + 1];
+ long actual = TimeValueConfig.durationOf(s);
+ assertEquals(s, expected.longValue(), actual);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java
new file mode 100644
index 0000000..d912998
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+public class AuthorizedKeyEntryLoginOptionsParseTest extends JUnitTestSupport {
+ private final String value;
+ private final String loginPart;
+ private final String keyPart;
+ private final Map<String, String> options;
+
+ public AuthorizedKeyEntryLoginOptionsParseTest(String value, String loginPart, String keyPart, Map<String, String> options) {
+ this.value = value;
+ this.loginPart = loginPart;
+ this.keyPart = keyPart;
+ this.options = options;
+ }
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> parameters() {
+ List<Object[]> params = new ArrayList<>();
+ addData(params, "ssh-rsa AAAAB2...19Q==", "john@example.net", "from=\"*.sales.example.net,!pc.sales.example.net\"");
+ addData(params, "ssh-dss AAAAC3...51R==", "example.net", "command=\"dump /home\"", "no-pty", "no-port-forwarding");
+ addData(params, "ssh-dss AAAAB5...21S==", "", "permitopen=\"192.0.2.1:80\"", "permitopen=\"192.0.2.2:25\"");
+ addData(params, "ssh-rsa AAAA...==", "jane@example.net", "tunnel=\"0\"", "command=\"sh /etc/netstart tun0\"");
+ addData(params, "ssh-rsa AAAA1C8...32Tv==", "user@example.net", "!restrict", "command=\"uptime\"");
+ addData(params, "ssh-rsa AAAA1f8...IrrC5==", "user@example.net", "restrict", "!pty", "command=\"nethack\"");
+ return params;
+ }
+
+ private static void addData(List<Object[]> params, String keyData, String comment, String... comps) {
+ StringBuilder sb = new StringBuilder();
+
+ Map<String, String> optionsMap = new HashMap<>();
+ for (String c : comps) {
+ if (sb.length() > 0) {
+ sb.append(',');
+ }
+ sb.append(c);
+
+ int pos = c.indexOf('=');
+ if (pos > 0) {
+ String name = c.substring(0, pos);
+ String value = GenericUtils.stripQuotes(c.substring(pos + 1)).toString();
+ String prev = optionsMap.put(name, value);
+ if (prev != null) {
+ optionsMap.put(name, prev + "," + value);
+ }
+ } else {
+ optionsMap.put(c, Boolean.toString(c.charAt(0) != AuthorizedKeyEntry.BOOLEAN_OPTION_NEGATION_INDICATOR));
+ }
+ }
+
+ int pos = sb.length();
+
+ sb.append(' ').append(keyData);
+ if (GenericUtils.isNotEmpty(comment)) {
+ sb.append(' ').append(comment);
+ }
+
+ String value = sb.toString();
+ params.add(new Object[] {value, value.substring(0, pos), value.substring(pos + 1), optionsMap});
+ }
+
+ @Test
+ public void testResolveEntryComponents() {
+ Map.Entry<String, String> actual = AuthorizedKeyEntry.resolveEntryComponents(value);
+ assertNotNull(value, actual);
+ assertEquals("login(" + value + ")", loginPart, actual.getKey());
+ assertEquals("remainder(" + value + ")", keyPart, actual.getValue());
+ }
+
+ @Test
+ public void testParseLoginOptions() {
+ Map<String, String> parsed = AuthorizedKeyEntry.parseLoginOptions(loginPart);
+ options.forEach((key, expected) -> {
+ String actual = parsed.get(key);
+ assertEquals(key, expected, actual);
+ });
+ assertEquals("Mismatched size", options.size(), parsed.size());
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" + value + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java
new file mode 100644
index 0000000..4735846
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.util.List;
+
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class BuiltinIdentitiesTest extends JUnitTestSupport {
+ private final BuiltinIdentities expected;
+
+ public BuiltinIdentitiesTest(BuiltinIdentities expected) {
+ this.expected = expected;
+ }
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> parameters() {
+ return parameterize(BuiltinIdentities.VALUES);
+ }
+
+ @BeforeClass // Dirty hack around the parameterized run
+ public static void testAllConstantsCovered() throws Exception {
+ Field[] fields = BuiltinIdentities.Constants.class.getFields();
+ for (Field f : fields) {
+ int mods = f.getModifiers();
+ if (!Modifier.isStatic(mods)) {
+ continue;
+ }
+
+ if (!Modifier.isFinal(mods)) {
+ continue;
+ }
+
+ Class<?> type = f.getType();
+ if (!String.class.isAssignableFrom(type)) {
+ continue;
+ }
+
+ String name = f.getName();
+ String value = (String) f.get(null);
+ BuiltinIdentities id = BuiltinIdentities.fromName(value);
+ assertNotNull("No match found for field " + name + "=" + value, id);
+ }
+ }
+
+ @Test
+ public void testFromName() {
+ String name = expected.getName();
+ for (int index = 0, count = name.length(); index < count; index++) {
+ assertSame(name, expected, BuiltinIdentities.fromName(name));
+ name = shuffleCase(name);
+ }
+ }
+
+ @Test
+ public void testFromAlgorithm() {
+ String algorithm = expected.getAlgorithm();
+ for (int index = 0, count = algorithm.length(); index < count; index++) {
+ assertSame(algorithm, expected, BuiltinIdentities.fromAlgorithm(algorithm));
+ algorithm = shuffleCase(algorithm);
+ }
+ }
+
+ @Test
+ public void testFromKey() throws GeneralSecurityException {
+ Assume.assumeTrue("Unsupported built-in identity", expected.isSupported());
+ KeyPairGenerator gen = SecurityUtils.getKeyPairGenerator(expected.getAlgorithm());
+ KeyPair kp = gen.generateKeyPair();
+ outputDebugMessage("Checking built-in identity: %s", expected);
+ assertSame(expected + "[pair]", expected, BuiltinIdentities.fromKeyPair(kp));
+ assertSame(expected + "[public]", expected, BuiltinIdentities.fromKey(kp.getPublic()));
+ assertSame(expected + "[private]", expected, BuiltinIdentities.fromKey(kp.getPrivate()));
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java
new file mode 100644
index 0000000..eedbfe7
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.KeyPair;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.AfterClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class KeyRandomArtTest extends JUnitTestSupport {
+ private static final Collection<KeyPair> KEYS = new LinkedList<>();
+
+ private final String algorithm;
+ private final int keySize;
+ private final KeyPair keyPair;
+
+ public KeyRandomArtTest(String algorithm, int keySize) throws Exception {
+ this.algorithm = algorithm;
+ this.keySize = keySize;
+ this.keyPair = CommonTestSupportUtils.generateKeyPair(algorithm, keySize);
+ KEYS.add(this.keyPair);
+ }
+
+ @Parameters(name = "algorithm={0}, key-size={1}")
+ public static List<Object[]> parameters() {
+ List<Object[]> params = new ArrayList<>();
+ for (int keySize : RSA_SIZES) {
+ params.add(new Object[]{KeyUtils.RSA_ALGORITHM, keySize});
+ }
+
+ for (int keySize : DSS_SIZES) {
+ params.add(new Object[]{KeyUtils.DSS_ALGORITHM, keySize});
+ }
+
+ if (SecurityUtils.isECCSupported()) {
+ for (ECCurves curve : ECCurves.VALUES) {
+ params.add(new Object[]{KeyUtils.EC_ALGORITHM, curve.getKeySize()});
+ }
+ }
+
+ if (SecurityUtils.isEDDSACurveSupported()) {
+ for (int keySize : ED25519_SIZES) {
+ params.add(new Object[]{SecurityUtils.EDDSA, keySize});
+ }
+ }
+ return params;
+ }
+
+ @AfterClass
+ public static void dumpAllArts() throws Exception {
+ KeyRandomArt.combine(System.out, ' ', () -> KEYS);
+ }
+
+ @Test
+ public void testRandomArtString() throws Exception {
+ KeyRandomArt art = new KeyRandomArt(keyPair.getPublic());
+ assertEquals("Mismatched algorithm", algorithm, art.getAlgorithm());
+ assertEquals("Mismatched key size", keySize, art.getKeySize());
+
+ String s = art.toString();
+ String[] lines = GenericUtils.split(s, '\n');
+ assertEquals("Mismatched lines count", KeyRandomArt.FLDSIZE_Y + 2, lines.length);
+
+ for (int index = 0; index < lines.length; index++) {
+ String l = lines[index];
+ if ((l.length() > 0) && (l.charAt(l.length() - 1) == '\r')) {
+ l = l.substring(0, l.length() - 1);
+ lines[index] = l;
+ }
+ System.out.append('\t').println(l);
+
+ assertTrue("Mismatched line length #" + (index + 1) + ": " + l.length(), l.length() >= (KeyRandomArt.FLDSIZE_X + 2));
+ }
+ }
+}
[35/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java
new file mode 100644
index 0000000..b665166
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.security;
+
+import java.security.Provider;
+import java.security.Security;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractSecurityProviderRegistrar
+ extends AbstractLoggingBean
+ implements SecurityProviderRegistrar {
+ protected final Map<String, Object> props = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ protected final Map<Class<?>, Map<String, Boolean>> supportedEntities = new HashMap<>();
+ protected final AtomicReference<Provider> providerHolder = new AtomicReference<>(null);
+
+ private final String name;
+
+ protected AbstractSecurityProviderRegistrar(String name) {
+ this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name provided");
+ }
+
+ @Override
+ public final String getName() {
+ return name;
+ }
+
+ @Override
+ public Map<String, Object> getProperties() {
+ return props;
+ }
+
+ @Override
+ public boolean isSecurityEntitySupported(Class<?> entityType, String name) {
+ Map<String, Boolean> supportMap;
+ synchronized (supportedEntities) {
+ supportMap = supportedEntities.computeIfAbsent(
+ entityType, k -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
+ }
+
+ Boolean supportFlag;
+ synchronized (supportMap) {
+ supportFlag = supportMap.computeIfAbsent(
+ name, k -> SecurityProviderRegistrar.super.isSecurityEntitySupported(entityType, name));
+ }
+
+ return supportFlag;
+ }
+
+ /**
+ * Attempts to see if a provider with this name already registered. If not,
+ * then uses reflection API in order to load and instantiate the specified
+ * <tt>providerClassName</tt>
+ *
+ * @param providerClassName The fully-qualified class name to instantiate
+ * if a provider not already registered
+ * @return The resolved {@link Provider} instance - <B>Note:</B> the result
+ * is <U>cached</U> - i.e., successful resolution result will not cause
+ * the code to re-resolve the provider
+ * @throws ReflectiveOperationException If failed to instantiate the provider
+ * @throws UnsupportedOperationException If registrar not supported
+ * @see #isSupported()
+ * @see Security#getProvider(String)
+ * @see #createProviderInstance(String)
+ */
+ protected Provider getOrCreateProvider(String providerClassName) throws ReflectiveOperationException {
+ if (!isSupported()) {
+ throw new UnsupportedOperationException("Provider not supported");
+ }
+
+ Provider provider;
+ boolean created = false;
+ synchronized (providerHolder) {
+ provider = providerHolder.get();
+ if (provider != null) {
+ return provider;
+ }
+
+ provider = Security.getProvider(getName());
+ if (provider == null) {
+ provider = createProviderInstance(providerClassName);
+ created = true;
+ }
+ providerHolder.set(provider);
+ }
+
+ if (created) {
+ log.info("getOrCreateProvider({}) created instance of {}", getName(), providerClassName);
+ } else {
+ log.info("getOrCreateProvider({}) resolved instance of {}", getName(), provider.getClass().getName());
+ }
+
+ return provider;
+ }
+
+ protected Provider createProviderInstance(String providerClassName) throws ReflectiveOperationException {
+ return SecurityProviderChoice.createProviderInstance(getClass(), providerClassName);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" + getName() + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
new file mode 100644
index 0000000..94b9454
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.security;
+
+import java.lang.reflect.Method;
+import java.security.GeneralSecurityException;
+import java.security.Provider;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @param <T> Type of security entity being generated by this factory
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SecurityEntityFactory<T> {
+ Class<T> getEntityType();
+
+ T getInstance(String algorithm) throws GeneralSecurityException;
+
+ /**
+ * Uses reflection in order to wrap the {@code getInstance} method(s)
+ * as a security entity factory.
+ *
+ * @param <F> Type of entity being generated by the factor
+ * @param entityType The entity type class
+ * @param registrar The {@code SecurityProviderRegistrar} to use - if
+ * {@code null} then default provider is used (if specified).
+ * @param defaultProvider Default provider choice to use if no registrar
+ * provided. If {@code null}/empty then JCE default is used
+ * @return The {@link SecurityEntityFactory} for the entity
+ * @throws ReflectiveOperationException If failed to create the factory
+ * @see #toDefaultFactory(Class)
+ * @see #toNamedProviderFactory(Class, String)
+ * @see #toProviderInstanceFactory(Class, Provider)
+ * @see SecurityProviderChoice#isNamedProviderUsed()
+ * @see SecurityProviderChoice#getSecurityProvider()
+ */
+ static <F> SecurityEntityFactory<F> toFactory(
+ Class<F> entityType, SecurityProviderChoice registrar, SecurityProviderChoice defaultProvider)
+ throws ReflectiveOperationException {
+ if (registrar == null) {
+ if ((defaultProvider == null) || (defaultProvider == SecurityProviderChoice.EMPTY)) {
+ return toDefaultFactory(entityType);
+ } else if (defaultProvider.isNamedProviderUsed()) {
+ return toNamedProviderFactory(entityType, defaultProvider.getName());
+ } else {
+ return toProviderInstanceFactory(entityType, defaultProvider.getSecurityProvider());
+ }
+ } else if (registrar.isNamedProviderUsed()) {
+ return toNamedProviderFactory(entityType, registrar.getName());
+ } else {
+ return toProviderInstanceFactory(entityType, registrar.getSecurityProvider());
+ }
+ }
+
+ static <F> SecurityEntityFactory<F> toDefaultFactory(Class<F> entityType)
+ throws ReflectiveOperationException {
+ Method m = entityType.getDeclaredMethod("getInstance", String.class);
+ return new SecurityEntityFactory<F>() {
+ private final String s = SecurityEntityFactory.class.getSimpleName()
+ + "[" + entityType.getSimpleName() + "]"
+ + "[default]";
+
+ @Override
+ public Class<F> getEntityType() {
+ return entityType;
+ }
+
+ @Override
+ public F getInstance(String algorithm) throws GeneralSecurityException {
+ try {
+ Object value = m.invoke(null, algorithm);
+ return entityType.cast(value);
+ } catch (ReflectiveOperationException t) {
+ Throwable e = GenericUtils.peelException(t);
+ if (e instanceof GeneralSecurityException) {
+ throw (GeneralSecurityException) e;
+ } else if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ } else if (e instanceof Error) {
+ throw (Error) e;
+ } else {
+ throw new GeneralSecurityException(e);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return s;
+ }
+ };
+ }
+
+ static <F> SecurityEntityFactory<F> toNamedProviderFactory(Class<F> entityType, String name)
+ throws ReflectiveOperationException {
+ ValidateUtils.checkNotNullAndNotEmpty(name, "No provider name specified");
+ Method m = entityType.getDeclaredMethod("getInstance", String.class, String.class);
+ return new SecurityEntityFactory<F>() {
+ private final String s = SecurityEntityFactory.class.getSimpleName()
+ + "[" + entityType.getSimpleName() + "]"
+ + "[" + name + "]";
+
+ @Override
+ public Class<F> getEntityType() {
+ return entityType;
+ }
+
+ @Override
+ public F getInstance(String algorithm) throws GeneralSecurityException {
+ try {
+ Object value = m.invoke(null, algorithm, name);
+ return entityType.cast(value);
+ } catch (ReflectiveOperationException t) {
+ Throwable e = GenericUtils.peelException(t);
+ if (e instanceof GeneralSecurityException) {
+ throw (GeneralSecurityException) e;
+ } else if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ } else if (e instanceof Error) {
+ throw (Error) e;
+ } else {
+ throw new GeneralSecurityException(e);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return s;
+ }
+ };
+ }
+
+ static <F> SecurityEntityFactory<F> toProviderInstanceFactory(Class<F> entityType, Provider provider)
+ throws ReflectiveOperationException {
+ Objects.requireNonNull(provider, "No provider instance");
+ Method m = entityType.getDeclaredMethod("getInstance", String.class, Provider.class);
+ return new SecurityEntityFactory<F>() {
+ private final String s = SecurityEntityFactory.class.getSimpleName()
+ + "[" + entityType.getSimpleName() + "]"
+ + "[" + Provider.class.getSimpleName() + "]"
+ + "[" + provider.getName() + "]";
+
+ @Override
+ public Class<F> getEntityType() {
+ return entityType;
+ }
+
+ @Override
+ public F getInstance(String algorithm) throws GeneralSecurityException {
+ try {
+ Object value = m.invoke(null, algorithm, provider);
+ return entityType.cast(value);
+ } catch (ReflectiveOperationException t) {
+ Throwable e = GenericUtils.peelException(t);
+ if (e instanceof GeneralSecurityException) {
+ throw (GeneralSecurityException) e;
+ } else if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ } else if (e instanceof Error) {
+ throw (Error) e;
+ } else {
+ throw new GeneralSecurityException(e);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return s;
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java
new file mode 100644
index 0000000..c12e747
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.security;
+
+import java.security.Provider;
+import java.util.Objects;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SecurityProviderChoice extends NamedResource {
+ SecurityProviderChoice EMPTY = new SecurityProviderChoice() {
+ @Override
+ public String getName() {
+ return null;
+ }
+
+ @Override
+ public boolean isNamedProviderUsed() {
+ return false;
+ }
+
+ @Override
+ public Provider getSecurityProvider() {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "EMPTY";
+ }
+ };
+
+ /**
+ * @return {@code true} if to use the provider's name rather than its
+ * {@link Provider} instance - default={@code true}.
+ */
+ default boolean isNamedProviderUsed() {
+ return true;
+ }
+
+ /**
+ * @return The security {@link Provider} to use in case {@link #isNamedProviderUsed()}
+ * is {@code false}. Can be {@code null} if {@link #isNamedProviderUsed()} is {@code true},
+ * but not recommended.
+ */
+ Provider getSecurityProvider();
+
+ static SecurityProviderChoice toSecurityProviderChoice(String name) {
+ ValidateUtils.checkNotNullAndNotEmpty(name, "No name provided");
+ return new SecurityProviderChoice() {
+ private final String s = SecurityProviderChoice.class.getSimpleName() + "[" + name + "]";
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isNamedProviderUsed() {
+ return true;
+ }
+
+ @Override
+ public Provider getSecurityProvider() {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return s;
+ }
+ };
+ }
+
+ static SecurityProviderChoice toSecurityProviderChoice(Provider provider) {
+ Objects.requireNonNull(provider, "No provider instance");
+ return new SecurityProviderChoice() {
+ private final String s = SecurityProviderChoice.class.getSimpleName()
+ + "[" + Provider.class.getSimpleName() + "]"
+ + "[" + provider.getName() + "]";
+
+ @Override
+ public String getName() {
+ return provider.getName();
+ }
+
+ @Override
+ public boolean isNamedProviderUsed() {
+ return false;
+ }
+
+ @Override
+ public Provider getSecurityProvider() {
+ return provider;
+ }
+
+ @Override
+ public String toString() {
+ return s;
+ }
+ };
+ }
+
+ static Provider createProviderInstance(Class<?> anchor, String providerClassName)
+ throws ReflectiveOperationException {
+ return ThreadUtils.createDefaultInstance(anchor, Provider.class, providerClassName);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java
new file mode 100644
index 0000000..31e428b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java
@@ -0,0 +1,337 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.security;
+
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.Provider;
+import java.security.Security;
+import java.security.Signature;
+import java.security.cert.CertificateFactory;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.Mac;
+
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.PropertyResolver;
+import org.apache.sshd.common.PropertyResolverUtils;
+import org.apache.sshd.common.SyspropsMapWrapper;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.IgnoringEmptyMap;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SecurityProviderRegistrar extends SecurityProviderChoice, OptionalFeature, PropertyResolver {
+ /**
+ * Base name for configuration properties related to security providers
+ */
+ String CONFIG_PROP_BASE = "org.apache.sshd.security.provider";
+
+ /**
+ * Property used to configure whether the provider is enabled regardless of
+ * whether it is supported.
+ *
+ * @see #isEnabled()
+ */
+ String ENABLED_PROPERTY = "enabled";
+
+ /**
+ * Property used to configure whether to use the provider's name rather than its
+ * {@link Provider} instance
+ *
+ * @see #isNamedProviderUsed()
+ */
+ String NAMED_PROVIDER_PROPERTY = "useNamed";
+
+ String ALL_OPTIONS_VALUE = "all";
+ String ALL_OPTIONS_WILDCARD = "*";
+
+ String NO_OPTIONS_VALUE = "none";
+
+ /**
+ * All the entities that are used in calls to {@link #isSecurityEntitySupported(Class, String)}
+ */
+ List<Class<?>> SECURITY_ENTITIES =
+ Collections.unmodifiableList(
+ Arrays.asList(
+ Cipher.class, KeyFactory.class, MessageDigest.class,
+ KeyPairGenerator.class, KeyAgreement.class, Mac.class,
+ Signature.class, CertificateFactory.class));
+
+ default String getBasePropertyName() {
+ return CONFIG_PROP_BASE + "." + getName();
+ }
+
+ default String getConfigurationPropertyName(String name) {
+ return getBasePropertyName() + "." + name;
+ }
+
+ /**
+ * @return {@code true} if the provider is enabled regardless of
+ * whether it is supported - default={@code true}. <B>Note:</B>
+ * checks if the provider has been <U>programmatically</U> disabled
+ * via {@link SecurityUtils#setAPrioriDisabledProvider(String, boolean)}
+ * @see #ENABLED_PROPERTY
+ */
+ default boolean isEnabled() {
+ if (SecurityUtils.isAPrioriDisabledProvider(getName())) {
+ return false;
+ }
+
+ return this.getBooleanProperty(getConfigurationPropertyName(ENABLED_PROPERTY), true);
+ }
+
+ @Override
+ default PropertyResolver getParentPropertyResolver() {
+ return SyspropsMapWrapper.SYSPROPS_RESOLVER;
+ }
+
+ @Override
+ default Map<String, Object> getProperties() {
+ return IgnoringEmptyMap.getInstance();
+ }
+
+ /**
+ * @param transformation The requested {@link Cipher} transformation
+ * @return {@code true} if this security provider supports the transformation
+ * @see #isSecurityEntitySupported(Class, String)
+ */
+ default boolean isCipherSupported(String transformation) {
+ return isSecurityEntitySupported(Cipher.class, transformation);
+ }
+
+ /**
+ * @param algorithm The {@link KeyFactory} algorithm
+ * @return {@code true} if this security provider supports the algorithm
+ * @see #isSecurityEntitySupported(Class, String)
+ */
+ default boolean isKeyFactorySupported(String algorithm) {
+ return isSecurityEntitySupported(KeyFactory.class, algorithm);
+ }
+
+ /**
+ * @param algorithm The {@link MessageDigest} algorithm
+ * @return {@code true} if this security provider supports the algorithm
+ * @see #isSecurityEntitySupported(Class, String)
+ */
+ default boolean isMessageDigestSupported(String algorithm) {
+ return isSecurityEntitySupported(MessageDigest.class, algorithm);
+ }
+
+ /**
+ * @param algorithm The {@link KeyPairGenerator} algorithm
+ * @return {@code true} if this security provider supports the algorithm
+ * @see #isSecurityEntitySupported(Class, String)
+ */
+ default boolean isKeyPairGeneratorSupported(String algorithm) {
+ return isSecurityEntitySupported(KeyPairGenerator.class, algorithm);
+ }
+
+ /**
+ * @param algorithm The {@link KeyAgreement} algorithm
+ * @return {@code true} if this security provider supports the algorithm
+ * @see #isSecurityEntitySupported(Class, String)
+ */
+ default boolean isKeyAgreementSupported(String algorithm) {
+ return isSecurityEntitySupported(KeyAgreement.class, algorithm);
+ }
+
+ /**
+ * @param algorithm The {@link Mac} algorithm
+ * @return {@code true} if this security provider supports the algorithm
+ * @see #isSecurityEntitySupported(Class, String)
+ */
+ default boolean isMacSupported(String algorithm) {
+ return isSecurityEntitySupported(Mac.class, algorithm);
+ }
+
+ /**
+ * @param algorithm The {@link Signature} algorithm
+ * @return {@code true} if this security provider supports the algorithm
+ * @see #isSecurityEntitySupported(Class, String)
+ */
+ default boolean isSignatureSupported(String algorithm) {
+ return isSecurityEntitySupported(Signature.class, algorithm);
+ }
+
+ /**
+ * @param type The {@link CertificateFactory} type
+ * @return {@code true} if this security provider supports the algorithm
+ * @see #isSecurityEntitySupported(Class, String)
+ */
+ default boolean isCertificateFactorySupported(String type) {
+ return isSecurityEntitySupported(CertificateFactory.class, type);
+ }
+
+ /**
+ * @param entityType The requested entity type - its simple name serves to
+ * build the configuration property name.
+ * @return Configuration value to use if no specific configuration provided
+ * - default=empty
+ * @see #isSecurityEntitySupported(Class, String)
+ */
+ default String getDefaultSecurityEntitySupportValue(Class<?> entityType) {
+ return "";
+ }
+
+ default boolean isSecurityEntitySupported(Class<?> entityType, String name) {
+ String defaultValue = getDefaultSecurityEntitySupportValue(entityType);
+ return isSecurityEntitySupported(this, entityType, name, defaultValue);
+ }
+
+ /**
+ * @return {@code true} if to use the provider's name rather than its
+ * {@link Provider} instance - default={@code true}
+ * @see #NAMED_PROVIDER_PROPERTY
+ * @see #getSecurityProvider()
+ * @see #registerSecurityProvider(SecurityProviderRegistrar)
+ */
+ @Override
+ default boolean isNamedProviderUsed() {
+ return PropertyResolverUtils.getBooleanProperty(this,
+ getConfigurationPropertyName(NAMED_PROVIDER_PROPERTY),
+ SecurityProviderChoice.super.isNamedProviderUsed());
+ }
+
+ /**
+ * @param v Value to be examined
+ * @return {@code true} if the value equals (case insensitive) to
+ * either {@link #ALL_OPTIONS_VALUE} or {@link #ALL_OPTIONS_WILDCARD}
+ */
+ static boolean isAllOptionsValue(String v) {
+ return ALL_OPTIONS_VALUE.equalsIgnoreCase(v)
+ || ALL_OPTIONS_WILDCARD.equalsIgnoreCase(v);
+ }
+
+ /**
+ * Checks whether the requested entity type algorithm/name is listed
+ * as supported by the registrar's configuration
+ *
+ * @param registrar The {@link SecurityProviderRegistrar}
+ * @param entityType The requested entity type - its simple name serves to
+ * build the configuration property name.
+ * @param name The requested algorithm/name - <B>Note:</B> if the requested
+ * entity is a {@link Cipher} then the argument is assumed to be a possible
+ * "/" separated transformation and parsed as such in order to
+ * retrieve the pure cipher name
+ * @param defaultValue Configuration value to use if no specific configuration provided
+ * @return {@code true} registrar is supported and the value is listed
+ * (case <U>insensitive</U>) or * the property is one of the "all" markers
+ * @see SecurityProviderRegistrar#isSupported()
+ * @see #isAllOptionsValue(String)
+ */
+ static boolean isSecurityEntitySupported(SecurityProviderRegistrar registrar, Class<?> entityType, String name, String defaultValue) {
+ return Objects.requireNonNull(registrar, "No registrar instance").isSupported()
+ && isSecurityEntitySupported(registrar, registrar.getConfigurationPropertyName(entityType.getSimpleName()), entityType, name, defaultValue);
+ }
+
+ static boolean isSecurityEntitySupported(PropertyResolver resolver, String propName, Class<?> entityType, String name, String defaultValue) {
+ if (GenericUtils.isEmpty(name)) {
+ return false;
+ }
+
+ String propValue = resolver.getString(propName);
+ if (GenericUtils.isEmpty(propValue)) {
+ propValue = defaultValue;
+ }
+
+ if (NO_OPTIONS_VALUE.equalsIgnoreCase(propValue)) {
+ return false;
+ }
+
+ String[] values = GenericUtils.split(propValue, ',');
+ if (GenericUtils.isEmpty(values)) {
+ return false;
+ }
+
+ if ((values.length == 1) && isAllOptionsValue(values[0])) {
+ return true;
+ }
+
+ String effectiveName = getEffectiveSecurityEntityName(entityType, name);
+ int index = Arrays.binarySearch(values, effectiveName, String.CASE_INSENSITIVE_ORDER);
+ return index >= 0;
+ }
+
+ /**
+ * Determines the "pure" security entity name - e.g., for {@link Cipher}s
+ * it strips the trailing transformation specification in order to extract the
+ * base cipher name - e.g., "AES/CBC/NoPadding" => "AES"
+ *
+ * @param entityType The security entity type - ignored if {@code null}
+ * @param name The effective name - ignored if {@code null}/empty
+ * @return The resolved name
+ */
+ static String getEffectiveSecurityEntityName(Class<?> entityType, String name) {
+ if ((entityType == null) || GenericUtils.isEmpty(name) || (!Cipher.class.isAssignableFrom(entityType))) {
+ return name;
+ }
+
+ int pos = name.indexOf('/');
+ return (pos > 0) ? name.substring(0, pos) : name;
+ }
+
+ /**
+ * Attempts to register the security provider represented by the registrar
+ * if not already registered. <B>Note:</B> if {@link SecurityProviderRegistrar#isNamedProviderUsed()}
+ * is {@code true} then the generated provider will be added to the system's
+ * list of known providers.
+ *
+ * @param registrar The {@link SecurityProviderRegistrar}
+ * @return {@code true} if no provider was previously registered
+ * @see Security#getProvider(String)
+ * @see SecurityProviderRegistrar#getSecurityProvider()
+ * @see Security#addProvider(Provider)
+ */
+ static boolean registerSecurityProvider(SecurityProviderRegistrar registrar) {
+ String name = ValidateUtils.checkNotNullAndNotEmpty(
+ (registrar == null) ? null : registrar.getName(), "No name for registrar=%s", registrar);
+ Provider p = Security.getProvider(name);
+ if (p != null) {
+ return false;
+ }
+
+ p = ValidateUtils.checkNotNull(
+ registrar.getSecurityProvider(), "No provider created for registrar of %s", name);
+ if (registrar.isNamedProviderUsed()) {
+ Security.addProvider(p);
+ }
+
+ return true;
+ }
+
+ static SecurityProviderRegistrar findSecurityProviderRegistrarBySecurityEntity(
+ Predicate<? super SecurityProviderRegistrar> entitySelector,
+ Collection<? extends SecurityProviderRegistrar> registrars) {
+ return GenericUtils.findFirstMatchingMember(
+ r -> r.isEnabled() && r.isSupported() && entitySelector.test(r), registrars);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
new file mode 100644
index 0000000..ee755e6
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
@@ -0,0 +1,759 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.security;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.cert.CertificateFactory;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Predicate;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.Mac;
+import javax.crypto.spec.DHParameterSpec;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+import org.apache.sshd.common.config.keys.loader.openssh.OpenSSHKeyPairResourceParser;
+import org.apache.sshd.common.config.keys.loader.pem.PEMResourceParserUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.random.JceRandomFactory;
+import org.apache.sshd.common.random.RandomFactory;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleGeneratorHostKeyProvider;
+import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleKeyPairResourceParser;
+import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleRandomFactory;
+import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderUtils;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Specific security providers related code
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class SecurityUtils {
+ /**
+ * Bouncycastle JCE provider name
+ */
+ public static final String BOUNCY_CASTLE = "BC";
+
+ /**
+ * EDDSA support - should match {@code EdDSAKey.KEY_ALGORITHM}
+ */
+ public static final String EDDSA = "EdDSA";
+
+ // A copy-paste from the original, but we don't want to drag the classes into the classpath
+ // See EdDSAEngine.SIGNATURE_ALGORITHM
+ public static final String CURVE_ED25519_SHA512 = "NONEwithEdDSA";
+
+ /**
+ * System property used to configure the value for the maximum supported Diffie-Hellman
+ * Group Exchange key size. If not set, then an internal auto-discovery mechanism is employed.
+ * If set to negative value then Diffie-Hellman Group Exchange is disabled. If set to a
+ * negative value then Diffie-Hellman Group Exchange is disabled
+ */
+ public static final String MAX_DHGEX_KEY_SIZE_PROP = "org.apache.sshd.maxDHGexKeySize";
+
+ /**
+ * The min. key size value used for testing whether Diffie-Hellman Group Exchange
+ * is supported or not. According to <A HREF="https://tools.ietf.org/html/rfc4419">RFC 4419</A>
+ * section 3: "Servers and clients SHOULD support groups with a modulus length of k
+ * bits, where 1024 <= k <= 8192".
+ * </code>
+ */
+ public static final int MIN_DHGEX_KEY_SIZE = 1024;
+ // Keys of size > 1024 are not support by default with JCE
+ public static final int DEFAULT_DHGEX_KEY_SIZE = MIN_DHGEX_KEY_SIZE;
+ public static final int PREFERRED_DHGEX_KEY_SIZE = 4096;
+ public static final int MAX_DHGEX_KEY_SIZE = 8192;
+
+ /**
+ * Comma separated list of fully qualified {@link SecurityProviderRegistrar}s
+ * to automatically register
+ */
+ public static final String SECURITY_PROVIDER_REGISTRARS = "org.apache.sshd.security.registrars";
+ public static final List<String> DEFAULT_SECURITY_PROVIDER_REGISTRARS =
+ Collections.unmodifiableList(
+ Arrays.asList(
+ "org.apache.sshd.common.util.security.bouncycastle.BouncyCastleSecurityProviderRegistrar",
+ "org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderRegistrar"));
+
+
+ /**
+ * System property used to control whether to automatically register the
+ * {@code Bouncyastle} JCE provider
+ * @deprecated Please use "org.apache.sshd.security.provider.BC.enabled"
+ */
+ @Deprecated
+ public static final String REGISTER_BOUNCY_CASTLE_PROP = "org.apache.sshd.registerBouncyCastle";
+
+ /**
+ * System property used to control whether Elliptic Curves are supported or not.
+ * If not set then the support is auto-detected. <B>Note:</B> if set to {@code true}
+ * it is up to the user to make sure that indeed there is a provider for them
+ */
+ public static final String ECC_SUPPORTED_PROP = "org.apache.sshd.eccSupport";
+
+ /**
+ * System property used to decide whether EDDSA curves are supported or not
+ * (in addition or even in spite of {@link #isEDDSACurveSupported()}). If not
+ * set or set to {@code true}, then the existence of the optional support classes
+ * determines the support.
+ * @deprecated Please use "org.apache.sshd.security.provider.EdDSA.enabled&qupt;
+ */
+ @Deprecated
+ public static final String EDDSA_SUPPORTED_PROP = "org.apache.sshd.eddsaSupport";
+
+ public static final String PROP_DEFAULT_SECURITY_PROVIDER = "org.apache.sshd.security.defaultProvider";
+
+ private static final AtomicInteger MAX_DHG_KEY_SIZE_HOLDER = new AtomicInteger(0);
+
+ /*
+ * NOTE: we use a LinkedHashMap in order to preserve registration order
+ * in case several providers support the same security entity
+ */
+ private static final Map<String, SecurityProviderRegistrar> REGISTERED_PROVIDERS = new LinkedHashMap<>();
+ private static final AtomicReference<KeyPairResourceParser> KEYPAIRS_PARSER_HODLER = new AtomicReference<>();
+ // If an entry already exists for the named provider, then it overrides its SecurityProviderRegistrar#isEnabled()
+ private static final Set<String> APRIORI_DISABLED_PROVIDERS = new TreeSet<>();
+ private static final AtomicBoolean REGISTRATION_STATE_HOLDER = new AtomicBoolean(false);
+ private static final Map<Class<?>, Map<String, SecurityEntityFactory<?>>> SECURITY_ENTITY_FACTORIES = new HashMap<>();
+
+ private static final AtomicReference<SecurityProviderChoice> DEFAULT_PROVIDER_HOLDER = new AtomicReference<>();
+
+ private static Boolean hasEcc;
+
+ private SecurityUtils() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ /**
+ * @param name The provider's name - never {@code null}/empty
+ * @return {@code true} if the provider is marked as disabled a-priori
+ * @see #setAPrioriDisabledProvider(String, boolean)
+ */
+ public static boolean isAPrioriDisabledProvider(String name) {
+ ValidateUtils.checkNotNullAndNotEmpty(name, "No provider name specified");
+ synchronized (APRIORI_DISABLED_PROVIDERS) {
+ return APRIORI_DISABLED_PROVIDERS.contains(name);
+ }
+ }
+
+ /**
+ * Marks a provider's registrar as "a-priori" <U>programatically</U>
+ * so that when its {@link SecurityProviderRegistrar#isEnabled()} is eventually
+ * consulted it will return {@code false} regardless of the configured value for
+ * the specific provider registrar instance. <B>Note:</B> has no effect if the
+ * provider has already been registered.
+ *
+ * @param name The provider's name - never {@code null}/empty
+ * @param disabled {@code true} whether to disable it a-priori
+ * @see #isAPrioriDisabledProvider(String)
+ */
+ public static void setAPrioriDisabledProvider(String name, boolean disabled) {
+ ValidateUtils.checkNotNullAndNotEmpty(name, "No provider name specified");
+ synchronized (APRIORI_DISABLED_PROVIDERS) {
+ if (disabled) {
+ APRIORI_DISABLED_PROVIDERS.add(name);
+ } else {
+ APRIORI_DISABLED_PROVIDERS.remove(name);
+ }
+ }
+ }
+
+ /**
+ * @return A <U>copy</U> if the current a-priori disabled providers names
+ */
+ public static Set<String> getAPrioriDisabledProviders() {
+ synchronized (APRIORI_DISABLED_PROVIDERS) {
+ return new TreeSet<>(APRIORI_DISABLED_PROVIDERS);
+ }
+ }
+
+ /**
+ * @return {@code true} if Elliptic Curve Cryptography is supported
+ * @see #ECC_SUPPORTED_PROP
+ */
+ public static boolean isECCSupported() {
+ if (hasEcc == null) {
+ String propValue = System.getProperty(ECC_SUPPORTED_PROP);
+ if (GenericUtils.isEmpty(propValue)) {
+ try {
+ getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
+ hasEcc = Boolean.TRUE;
+ } catch (Throwable t) {
+ hasEcc = Boolean.FALSE;
+ }
+ } else {
+ Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
+ logger.info("Override ECC support value: " + propValue);
+ hasEcc = Boolean.valueOf(propValue);
+ }
+ }
+
+ return hasEcc;
+ }
+
+ /**
+ * @return {@code true} if Diffie-Hellman Group Exchange is supported
+ * @see #getMaxDHGroupExchangeKeySize()
+ */
+ public static boolean isDHGroupExchangeSupported() {
+ return getMaxDHGroupExchangeKeySize() > 0;
+ }
+
+ /**
+ * @param keySize The expected key size
+ * @return {@code true} if Oakely Diffie-Hellman Group Exchange is supported
+ * for the specified key size
+ * @see #getMaxDHGroupExchangeKeySize()
+ */
+ public static boolean isDHOakelyGroupSupported(int keySize) {
+ return getMaxDHGroupExchangeKeySize() >= keySize;
+ }
+
+ /**
+ * @return The maximum supported Diffie-Hellman Group Exchange key size,
+ * or non-positive if not supported
+ */
+ public static int getMaxDHGroupExchangeKeySize() {
+ int maxSupportedKeySize;
+ synchronized (MAX_DHG_KEY_SIZE_HOLDER) {
+ maxSupportedKeySize = MAX_DHG_KEY_SIZE_HOLDER.get();
+ if (maxSupportedKeySize != 0) { // 1st time we are called ?
+ return maxSupportedKeySize;
+ }
+
+ String propValue = System.getProperty(MAX_DHGEX_KEY_SIZE_PROP);
+ if (GenericUtils.isEmpty(propValue)) {
+ maxSupportedKeySize = -1;
+ // Go down from max. to min. to ensure we stop at 1st maximum value success
+ for (int testKeySize = MAX_DHGEX_KEY_SIZE; testKeySize >= MIN_DHGEX_KEY_SIZE; testKeySize -= 1024) {
+ if (isDHGroupExchangeSupported(testKeySize)) {
+ maxSupportedKeySize = testKeySize;
+ break;
+ }
+ }
+ } else {
+ Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
+ logger.info("Override max. DH group exchange key size: " + propValue);
+ maxSupportedKeySize = Integer.parseInt(propValue);
+ // negative is OK - means user wants to disable DH group exchange
+ ValidateUtils.checkTrue(maxSupportedKeySize != 0,
+ "Configured " + MAX_DHGEX_KEY_SIZE_PROP + " value must be non-zero: %d", maxSupportedKeySize);
+ }
+
+ MAX_DHG_KEY_SIZE_HOLDER.set(maxSupportedKeySize);
+ }
+
+ return maxSupportedKeySize;
+ }
+
+ /**
+ * Set programmatically the reported value for {@link #getMaxDHGroupExchangeKeySize()}
+ * @param keySize The reported key size - if zero, then it will be auto-detected, if
+ * negative then DH group exchange will be disabled
+ */
+ public static void setMaxDHGroupExchangeKeySize(int keySize) {
+ synchronized (MAX_DHG_KEY_SIZE_HOLDER) {
+ MAX_DHG_KEY_SIZE_HOLDER.set(keySize);
+ }
+ }
+
+ public static boolean isDHGroupExchangeSupported(int maxKeySize) {
+ ValidateUtils.checkTrue(maxKeySize > Byte.SIZE, "Invalid max. key size: %d", maxKeySize);
+
+ try {
+ BigInteger r = new BigInteger("0").setBit(maxKeySize - 1);
+ DHParameterSpec dhSkipParamSpec = new DHParameterSpec(r, r);
+ KeyPairGenerator kpg = getKeyPairGenerator("DH");
+ kpg.initialize(dhSkipParamSpec);
+ return true;
+ } catch (GeneralSecurityException t) {
+ return false;
+ }
+ }
+
+ public static SecurityProviderChoice getDefaultProviderChoice() {
+ SecurityProviderChoice choice;
+ synchronized (DEFAULT_PROVIDER_HOLDER) {
+ choice = DEFAULT_PROVIDER_HOLDER.get();
+ if (choice != null) {
+ return choice;
+ }
+
+ String name = System.getProperty(PROP_DEFAULT_SECURITY_PROVIDER);
+ choice = (GenericUtils.isEmpty(name) || "none".equalsIgnoreCase(name))
+ ? SecurityProviderChoice.EMPTY
+ : SecurityProviderChoice.toSecurityProviderChoice(name);
+ DEFAULT_PROVIDER_HOLDER.set(choice);
+ }
+
+ return choice;
+ }
+
+ public static void setDefaultProviderChoice(SecurityProviderChoice choice) {
+ DEFAULT_PROVIDER_HOLDER.set(choice);
+ }
+
+ /**
+ * @return A <U>copy</U> of the currently registered security providers
+ */
+ public static Set<String> getRegisteredProviders() {
+ // returns a COPY of the providers in order to avoid modifications
+ synchronized (REGISTERED_PROVIDERS) {
+ return new TreeSet<>(REGISTERED_PROVIDERS.keySet());
+ }
+ }
+
+ public static boolean isBouncyCastleRegistered() {
+ register();
+ return isProviderRegistered(BOUNCY_CASTLE);
+ }
+
+ public static boolean isProviderRegistered(String provider) {
+ return getRegisteredProvider(provider) != null;
+ }
+
+ public static SecurityProviderRegistrar getRegisteredProvider(String provider) {
+ ValidateUtils.checkNotNullAndNotEmpty(provider, "No provider name specified");
+ synchronized (REGISTERED_PROVIDERS) {
+ return REGISTERED_PROVIDERS.get(provider);
+ }
+ }
+
+ public static boolean isRegistrationCompleted() {
+ return REGISTRATION_STATE_HOLDER.get();
+ }
+
+ private static void register() {
+ synchronized (REGISTRATION_STATE_HOLDER) {
+ if (REGISTRATION_STATE_HOLDER.get()) {
+ return;
+ }
+
+ String regsList = System.getProperty(SECURITY_PROVIDER_REGISTRARS,
+ GenericUtils.join(DEFAULT_SECURITY_PROVIDER_REGISTRARS, ','));
+ boolean bouncyCastleRegistered = false;
+ if ((GenericUtils.length(regsList) > 0) && (!"none".equalsIgnoreCase(regsList))) {
+ String[] classes = GenericUtils.split(regsList, ',');
+ Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
+ boolean debugEnabled = logger.isDebugEnabled();
+ for (String registrarClass : classes) {
+ SecurityProviderRegistrar r;
+ try {
+ r = ThreadUtils.createDefaultInstance(SecurityUtils.class, SecurityProviderRegistrar.class, registrarClass);
+ } catch (ReflectiveOperationException t) {
+ Throwable e = GenericUtils.peelException(t);
+ logger.error("Failed ({}) to create default {} registrar instance: {}",
+ e.getClass().getSimpleName(), registrarClass, e.getMessage());
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ } else if (e instanceof Error) {
+ throw (Error) e;
+ } else {
+ throw new RuntimeException(e);
+ }
+ }
+
+ String name = r.getName();
+ SecurityProviderRegistrar registeredInstance = registerSecurityProvider(r);
+ if (registeredInstance == null) {
+ if (debugEnabled) {
+ logger.debug("register({}) not registered - enabled={}, supported={}",
+ name, r.isEnabled(), r.isSupported());
+ }
+ continue; // provider not registered - e.g., disabled, not supported
+ }
+
+ if (BOUNCY_CASTLE.equalsIgnoreCase(name)) {
+ bouncyCastleRegistered = true;
+ }
+ }
+ }
+
+ SecurityProviderChoice choice = getDefaultProviderChoice();
+ if (((choice == null) || (choice == SecurityProviderChoice.EMPTY)) && bouncyCastleRegistered) {
+ setDefaultProviderChoice(SecurityProviderChoice.toSecurityProviderChoice(BOUNCY_CASTLE));
+ }
+
+ REGISTRATION_STATE_HOLDER.set(true);
+ }
+ }
+
+ /**
+ * @param registrar The registrar instance to register
+ * @return The registered instance - may be different than required
+ * if already registered. Returns {@code null} if not already registered
+ * and not enabled or not supported registrar.
+ */
+ public static SecurityProviderRegistrar registerSecurityProvider(SecurityProviderRegistrar registrar) {
+ Objects.requireNonNull(registrar, "No registrar instance to register");
+ String name = registrar.getName();
+ SecurityProviderRegistrar registeredInstance = getRegisteredProvider(name);
+ if ((registeredInstance == null) && registrar.isEnabled() && registrar.isSupported()) {
+ try {
+ SecurityProviderRegistrar.registerSecurityProvider(registrar);
+ synchronized (REGISTERED_PROVIDERS) {
+ REGISTERED_PROVIDERS.put(name, registrar);
+ }
+
+ return registrar;
+ } catch (Throwable t) {
+ Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
+ logger.error("Failed {} to register {} as a JCE provider: {}",
+ t.getClass().getSimpleName(), name, t.getMessage());
+ throw new RuntimeException("Failed to register " + name + " as a JCE provider", t);
+ }
+ }
+
+ return registeredInstance;
+ }
+
+ ///////////////// Bouncycastle specific implementations //////////////////
+
+ /* -------------------------------------------------------------------- */
+
+ /**
+ * @param resourceKey An identifier of the key being loaded - used as
+ * argument to the {@link FilePasswordProvider#getPassword(String)}
+ * invocation
+ * @param inputStream The {@link InputStream} for the <U>private</U> key
+ * @param provider A {@link FilePasswordProvider} - may be {@code null}
+ * if the loaded key is <U>guaranteed</U> not to be encrypted
+ * @return The loaded {@link KeyPair}
+ * @throws IOException If failed to read/parse the input stream
+ * @throws GeneralSecurityException If failed to generate the keys
+ */
+ public static KeyPair loadKeyPairIdentity(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
+ throws IOException, GeneralSecurityException {
+ KeyPairResourceParser parser = getKeyPairResourceParser();
+ if (parser == null) {
+ throw new NoSuchProviderException("No registered key-pair resource parser");
+ }
+
+ Collection<KeyPair> ids = parser.loadKeyPairs(resourceKey, provider, inputStream);
+ int numLoaded = GenericUtils.size(ids);
+ if (numLoaded <= 0) {
+ throw new InvalidKeyException("Unsupported private key file format: " + resourceKey);
+ }
+ if (numLoaded != 1) {
+ throw new InvalidKeySpecException("Multiple private key pairs N/A: " + resourceKey);
+ }
+
+ return ids.iterator().next();
+ }
+
+ /* -------------------------------------------------------------------- */
+
+ public static AbstractGeneratorHostKeyProvider createGeneratorHostKeyProvider(Path path) {
+ ValidateUtils.checkTrue(isBouncyCastleRegistered(), "BouncyCastle not registered");
+ return new BouncyCastleGeneratorHostKeyProvider(path);
+ }
+
+ public static KeyPairResourceParser getBouncycastleKeyPairResourceParser() {
+ ValidateUtils.checkTrue(isBouncyCastleRegistered(), "BouncyCastle not registered");
+ return BouncyCastleKeyPairResourceParser.INSTANCE;
+ }
+
+ /**
+ * @return If {@link #isBouncyCastleRegistered()} then a {@link BouncyCastleRandomFactory}
+ * instance, otherwise a {@link JceRandomFactory} one
+ */
+ public static RandomFactory getRandomFactory() {
+ if (isBouncyCastleRegistered()) {
+ return BouncyCastleRandomFactory.INSTANCE;
+ } else {
+ return JceRandomFactory.INSTANCE;
+ }
+ }
+
+ ///////////////////////////// ED25519 support ///////////////////////////////
+
+ /**
+ * @return {@code true} if EDDSA curves (e.g., {@code ed25519}) are supported
+ */
+ public static boolean isEDDSACurveSupported() {
+ register();
+
+ SecurityProviderRegistrar r = getRegisteredProvider(EDDSA);
+ return (r != null) && r.isEnabled() && r.isSupported();
+ }
+
+ /* -------------------------------------------------------------------- */
+
+ public static PublicKeyEntryDecoder<? extends PublicKey, ? extends PrivateKey> getEDDSAPublicKeyEntryDecoder() {
+ if (!isEDDSACurveSupported()) {
+ throw new UnsupportedOperationException(EDDSA + " provider N/A");
+ }
+
+ return EdDSASecurityProviderUtils.getEDDSAPublicKeyEntryDecoder();
+ }
+
+ public static PrivateKeyEntryDecoder<? extends PublicKey, ? extends PrivateKey> getOpenSSHEDDSAPrivateKeyEntryDecoder() {
+ if (!isEDDSACurveSupported()) {
+ throw new UnsupportedOperationException(EDDSA + " provider N/A");
+ }
+
+ return EdDSASecurityProviderUtils.getOpenSSHEDDSAPrivateKeyEntryDecoder();
+ }
+
+ public static org.apache.sshd.common.signature.Signature getEDDSASigner() {
+ if (isEDDSACurveSupported()) {
+ return EdDSASecurityProviderUtils.getEDDSASignature();
+ }
+
+ throw new UnsupportedOperationException(EDDSA + " Signer not available");
+ }
+
+ public static int getEDDSAKeySize(Key key) {
+ return EdDSASecurityProviderUtils.getEDDSAKeySize(key);
+ }
+
+ public static Class<? extends PublicKey> getEDDSAPublicKeyType() {
+ return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.getEDDSAPublicKeyType() : PublicKey.class;
+ }
+
+ public static Class<? extends PrivateKey> getEDDSAPrivateKeyType() {
+ return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.getEDDSAPrivateKeyType() : PrivateKey.class;
+ }
+
+ public static boolean compareEDDSAPPublicKeys(PublicKey k1, PublicKey k2) {
+ return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.compareEDDSAPPublicKeys(k1, k2) : false;
+ }
+
+ public static boolean compareEDDSAPrivateKeys(PrivateKey k1, PrivateKey k2) {
+ return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.compareEDDSAPrivateKeys(k1, k2) : false;
+ }
+
+ public static PublicKey recoverEDDSAPublicKey(PrivateKey key) throws GeneralSecurityException {
+ if (!isEDDSACurveSupported()) {
+ throw new NoSuchAlgorithmException(EDDSA + " provider not supported");
+ }
+
+ return EdDSASecurityProviderUtils.recoverEDDSAPublicKey(key);
+ }
+
+ public static PublicKey generateEDDSAPublicKey(String keyType, byte[] seed) throws GeneralSecurityException {
+ if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
+ throw new InvalidKeyException("Unsupported key type: " + keyType);
+ }
+
+ if (!isEDDSACurveSupported()) {
+ throw new NoSuchAlgorithmException(EDDSA + " provider not supported");
+ }
+
+ return EdDSASecurityProviderUtils.generateEDDSAPublicKey(seed);
+ }
+
+ public static <B extends Buffer> B putRawEDDSAPublicKey(B buffer, PublicKey key) {
+ if (!isEDDSACurveSupported()) {
+ throw new UnsupportedOperationException(EDDSA + " provider not supported");
+ }
+
+ return EdDSASecurityProviderUtils.putRawEDDSAPublicKey(buffer, key);
+ }
+
+ public static <B extends Buffer> B putEDDSAKeyPair(B buffer, KeyPair kp) {
+ return putEDDSAKeyPair(buffer, Objects.requireNonNull(kp, "No key pair").getPublic(), kp.getPrivate());
+ }
+
+ public static <B extends Buffer> B putEDDSAKeyPair(B buffer, PublicKey pubKey, PrivateKey prvKey) {
+ if (!isEDDSACurveSupported()) {
+ throw new UnsupportedOperationException(EDDSA + " provider not supported");
+ }
+
+ return EdDSASecurityProviderUtils.putEDDSAKeyPair(buffer, pubKey, prvKey);
+ }
+
+ public static KeyPair extractEDDSAKeyPair(Buffer buffer, String keyType) throws GeneralSecurityException {
+ if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
+ throw new InvalidKeyException("Unsupported key type: " + keyType);
+ }
+
+ if (!isEDDSACurveSupported()) {
+ throw new NoSuchAlgorithmException(EDDSA + " provider not supported");
+ }
+
+ throw new GeneralSecurityException("Full SSHD-440 implementation N/A");
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+
+ public static KeyPairResourceParser getKeyPairResourceParser() {
+ KeyPairResourceParser parser;
+ synchronized (KEYPAIRS_PARSER_HODLER) {
+ parser = KEYPAIRS_PARSER_HODLER.get();
+ if (parser != null) {
+ return parser;
+ }
+
+ parser = KeyPairResourceParser.aggregate(
+ PEMResourceParserUtils.PROXY,
+ OpenSSHKeyPairResourceParser.INSTANCE);
+ KEYPAIRS_PARSER_HODLER.set(parser);
+ }
+
+ return parser;
+ }
+
+ /**
+ * @param parser The system-wide {@code KeyPairResourceParser} to use.
+ * If set to {@code null}, then the default parser will be re-constructed
+ * on next call to {@link #getKeyPairResourceParser()}
+ */
+ public static void setKeyPairResourceParser(KeyPairResourceParser parser) {
+ synchronized (KEYPAIRS_PARSER_HODLER) {
+ KEYPAIRS_PARSER_HODLER.set(parser);
+ }
+ }
+
+ //////////////////////////// Security entities factories /////////////////////////////
+
+ @SuppressWarnings("unchecked")
+ public static <T> SecurityEntityFactory<T> resolveSecurityEntityFactory(
+ Class<T> entityType, String algorithm, Predicate<? super SecurityProviderRegistrar> entitySelector) {
+ Map<String, SecurityEntityFactory<?>> factoriesMap;
+ synchronized (SECURITY_ENTITY_FACTORIES) {
+ factoriesMap =
+ SECURITY_ENTITY_FACTORIES.computeIfAbsent(
+ entityType, k -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
+ }
+
+ String effectiveName = SecurityProviderRegistrar.getEffectiveSecurityEntityName(entityType, algorithm);
+ SecurityEntityFactory<?> factoryEntry;
+ synchronized (factoriesMap) {
+ factoryEntry =
+ factoriesMap.computeIfAbsent(
+ effectiveName, k -> createSecurityEntityFactory(entityType, entitySelector));
+ }
+
+ return (SecurityEntityFactory<T>) factoryEntry;
+ }
+
+ public static <T> SecurityEntityFactory<T> createSecurityEntityFactory(
+ Class<T> entityType, Predicate<? super SecurityProviderRegistrar> entitySelector) {
+ register();
+
+ SecurityProviderRegistrar registrar;
+ synchronized (REGISTERED_PROVIDERS) {
+ registrar =
+ SecurityProviderRegistrar.findSecurityProviderRegistrarBySecurityEntity(
+ entitySelector, REGISTERED_PROVIDERS.values());
+ }
+
+ try {
+ return SecurityEntityFactory.toFactory(entityType, registrar, getDefaultProviderChoice());
+ } catch (ReflectiveOperationException t) {
+ Throwable e = GenericUtils.peelException(t);
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ } else if (e instanceof Error) {
+ throw (Error) e;
+ } else {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public static KeyFactory getKeyFactory(String algorithm) throws GeneralSecurityException {
+ SecurityEntityFactory<KeyFactory> factory =
+ resolveSecurityEntityFactory(KeyFactory.class, algorithm, r -> r.isKeyFactorySupported(algorithm));
+ return factory.getInstance(algorithm);
+ }
+
+ public static Cipher getCipher(String transformation) throws GeneralSecurityException {
+ SecurityEntityFactory<Cipher> factory =
+ resolveSecurityEntityFactory(Cipher.class, transformation, r -> r.isCipherSupported(transformation));
+ return factory.getInstance(transformation);
+ }
+
+ public static MessageDigest getMessageDigest(String algorithm) throws GeneralSecurityException {
+ SecurityEntityFactory<MessageDigest> factory =
+ resolveSecurityEntityFactory(MessageDigest.class, algorithm, r -> r.isMessageDigestSupported(algorithm));
+ return factory.getInstance(algorithm);
+ }
+
+ public static KeyPairGenerator getKeyPairGenerator(String algorithm) throws GeneralSecurityException {
+ SecurityEntityFactory<KeyPairGenerator> factory =
+ resolveSecurityEntityFactory(KeyPairGenerator.class, algorithm, r -> r.isKeyPairGeneratorSupported(algorithm));
+ return factory.getInstance(algorithm);
+ }
+
+ public static KeyAgreement getKeyAgreement(String algorithm) throws GeneralSecurityException {
+ SecurityEntityFactory<KeyAgreement> factory =
+ resolveSecurityEntityFactory(KeyAgreement.class, algorithm, r -> r.isKeyAgreementSupported(algorithm));
+ return factory.getInstance(algorithm);
+ }
+
+ public static Mac getMac(String algorithm) throws GeneralSecurityException {
+ SecurityEntityFactory<Mac> factory =
+ resolveSecurityEntityFactory(Mac.class, algorithm, r -> r.isMacSupported(algorithm));
+ return factory.getInstance(algorithm);
+ }
+
+ public static Signature getSignature(String algorithm) throws GeneralSecurityException {
+ SecurityEntityFactory<Signature> factory =
+ resolveSecurityEntityFactory(Signature.class, algorithm, r -> r.isSignatureSupported(algorithm));
+ return factory.getInstance(algorithm);
+ }
+
+ public static CertificateFactory getCertificateFactory(String type) throws GeneralSecurityException {
+ SecurityEntityFactory<CertificateFactory> factory =
+ resolveSecurityEntityFactory(CertificateFactory.class, type, r -> r.isCertificateFactorySupported(type));
+ return factory.getInstance(type);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java
new file mode 100644
index 0000000..3716719
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.security.bouncycastle;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+
+import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BouncyCastleGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProvider {
+ public BouncyCastleGeneratorHostKeyProvider(Path path) {
+ setPath(path);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
+ try (org.bouncycastle.openssl.PEMWriter w =
+ new org.bouncycastle.openssl.PEMWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {
+ w.writeObject(kp);
+ w.flush();
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
new file mode 100644
index 0000000..4c8722a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.security.bouncycastle;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.NoSuchProviderException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.security.SecurityProviderRegistrar;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.bouncycastle.openssl.PEMDecryptorProvider;
+import org.bouncycastle.openssl.PEMEncryptedKeyPair;
+import org.bouncycastle.openssl.PEMKeyPair;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourceParser {
+ public static final List<String> BEGINNERS =
+ Collections.unmodifiableList(
+ Arrays.asList(
+ "BEGIN RSA PRIVATE KEY",
+ "BEGIN DSA PRIVATE KEY",
+ "BEGIN EC PRIVATE KEY"));
+
+ public static final List<String> ENDERS =
+ Collections.unmodifiableList(
+ Arrays.asList(
+ "END RSA PRIVATE KEY",
+ "END DSA PRIVATE KEY",
+ "END EC PRIVATE KEY"));
+
+ public static final BouncyCastleKeyPairResourceParser INSTANCE = new BouncyCastleKeyPairResourceParser();
+
+ public BouncyCastleKeyPairResourceParser() {
+ super(BEGINNERS, ENDERS);
+ }
+
+ @Override
+ public Collection<KeyPair> extractKeyPairs(
+ String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
+ throws IOException, GeneralSecurityException {
+ StringBuilder writer = new StringBuilder(beginMarker.length() + endMarker.length() + lines.size() * 80);
+ writer.append(beginMarker).append(IoUtils.EOL);
+ lines.forEach(l -> writer.append(l).append(IoUtils.EOL));
+ writer.append(endMarker).append(IoUtils.EOL);
+
+ String data = writer.toString();
+ byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
+ try (InputStream bais = new ByteArrayInputStream(dataBytes)) {
+ return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, bais);
+ }
+ }
+
+ @Override
+ public Collection<KeyPair> extractKeyPairs(
+ String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+ throws IOException, GeneralSecurityException {
+ KeyPair kp = loadKeyPair(resourceKey, stream, passwordProvider);
+ return (kp == null) ? Collections.emptyList() : Collections.singletonList(kp);
+ }
+
+ public static KeyPair loadKeyPair(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
+ throws IOException, GeneralSecurityException {
+ try (PEMParser r = new PEMParser(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+ Object o = r.readObject();
+
+ SecurityProviderRegistrar registrar = SecurityUtils.getRegisteredProvider(SecurityUtils.BOUNCY_CASTLE);
+ if (registrar == null) {
+ throw new NoSuchProviderException(SecurityUtils.BOUNCY_CASTLE + " registrar not available");
+ }
+
+ JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
+ if (registrar.isNamedProviderUsed()) {
+ pemConverter.setProvider(registrar.getName());
+ } else {
+ pemConverter.setProvider(registrar.getSecurityProvider());
+ }
+ if (o instanceof PEMEncryptedKeyPair) {
+ ValidateUtils.checkNotNull(provider, "No password provider for resource=%s", resourceKey);
+
+ String password = ValidateUtils.checkNotNullAndNotEmpty(provider.getPassword(resourceKey), "No password provided for resource=%s", resourceKey);
+ JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder();
+ PEMDecryptorProvider pemDecryptor = decryptorBuilder.build(password.toCharArray());
+ o = ((PEMEncryptedKeyPair) o).decryptKeyPair(pemDecryptor);
+ }
+
+ if (o instanceof PEMKeyPair) {
+ return pemConverter.getKeyPair((PEMKeyPair) o);
+ } else if (o instanceof KeyPair) {
+ return (KeyPair) o;
+ } else {
+ throw new IOException("Failed to read " + resourceKey + " - unknown result object: " + o);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandom.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandom.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandom.java
new file mode 100644
index 0000000..36c23e7
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandom.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.security.bouncycastle;
+
+import java.security.SecureRandom;
+
+import org.apache.sshd.common.random.AbstractRandom;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.bouncycastle.crypto.prng.RandomGenerator;
+import org.bouncycastle.crypto.prng.VMPCRandomGenerator;
+
+/**
+ * BouncyCastle <code>Random</code>.
+ * This pseudo random number generator uses the a very fast PRNG from BouncyCastle.
+ * The JRE random will be used when creating a new generator to add some random
+ * data to the seed.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class BouncyCastleRandom extends AbstractRandom {
+ public static final String NAME = SecurityUtils.BOUNCY_CASTLE;
+ private final RandomGenerator random;
+
+ public BouncyCastleRandom() {
+ ValidateUtils.checkTrue(SecurityUtils.isBouncyCastleRegistered(), "BouncyCastle not registered");
+ this.random = new VMPCRandomGenerator();
+ byte[] seed = new SecureRandom().generateSeed(8);
+ this.random.addSeedMaterial(seed);
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public void fill(byte[] bytes, int start, int len) {
+ this.random.nextBytes(bytes, start, len);
+ }
+
+ /**
+ * Returns a pseudo-random uniformly distributed {@code int}
+ * in the half-open range [0, n).
+ */
+ @Override
+ public int random(int n) {
+ ValidateUtils.checkTrue(n > 0, "Limit must be positive: %d", n);
+ if ((n & -n) == n) {
+ return (int) ((n * (long) next(31)) >> 31);
+ }
+
+ int bits;
+ int val;
+ do {
+ bits = next(31);
+ val = bits % n;
+ } while (bits - val + (n - 1) < 0);
+ return val;
+ }
+
+ private int next(int numBits) {
+ int bytes = (numBits + 7) / 8;
+ byte next[] = new byte[bytes];
+ int ret = 0;
+ random.nextBytes(next);
+ for (int i = 0; i < bytes; i++) {
+ ret = (next[i] & 0xFF) | (ret << 8);
+ }
+ return ret >>> (bytes * 8 - numBits);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandomFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandomFactory.java
new file mode 100644
index 0000000..720c7a5
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandomFactory.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.util.security.bouncycastle;
+
+import org.apache.sshd.common.random.AbstractRandomFactory;
+import org.apache.sshd.common.random.Random;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Named factory for the BouncyCastle <code>Random</code>
+ */
+public final class BouncyCastleRandomFactory extends AbstractRandomFactory {
+ public static final String NAME = "bouncycastle";
+ public static final BouncyCastleRandomFactory INSTANCE = new BouncyCastleRandomFactory();
+
+ public BouncyCastleRandomFactory() {
+ super(NAME);
+ }
+
+ @Override
+ public boolean isSupported() {
+ return SecurityUtils.isBouncyCastleRegistered();
+ }
+
+ @Override
+ public Random create() {
+ return new BouncyCastleRandom();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java
new file mode 100644
index 0000000..b47ca80
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.security.bouncycastle;
+
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.Provider;
+import java.security.Signature;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ReflectionUtils;
+import org.apache.sshd.common.util.security.AbstractSecurityProviderRegistrar;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BouncyCastleSecurityProviderRegistrar extends AbstractSecurityProviderRegistrar {
+ // We want to use reflection API so as not to require BouncyCastle to be present in the classpath
+ public static final String PROVIDER_CLASS = "org.bouncycastle.jce.provider.BouncyCastleProvider";
+ // Do not define a static registrar instance to minimize class loading issues
+ private final AtomicReference<Boolean> supportHolder = new AtomicReference<>(null);
+ private final AtomicReference<String> allSupportHolder = new AtomicReference<>();
+
+ public BouncyCastleSecurityProviderRegistrar() {
+ super(SecurityUtils.BOUNCY_CASTLE);
+ }
+
+ @Override
+ public boolean isEnabled() {
+ if (!super.isEnabled()) {
+ return false;
+ }
+
+ // For backward compatibility
+ return this.getBooleanProperty(SecurityUtils.REGISTER_BOUNCY_CASTLE_PROP, true);
+ }
+
+ @Override
+ public Provider getSecurityProvider() {
+ try {
+ return getOrCreateProvider(PROVIDER_CLASS);
+ } catch (ReflectiveOperationException t) {
+ Throwable e = GenericUtils.peelException(t);
+ log.error("getSecurityProvider({}) failed ({}) to instantiate {}: {}",
+ getName(), e.getClass().getSimpleName(), PROVIDER_CLASS, e.getMessage());
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ }
+
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public String getDefaultSecurityEntitySupportValue(Class<?> entityType) {
+ String allValue = allSupportHolder.get();
+ if (GenericUtils.length(allValue) > 0) {
+ return allValue;
+ }
+
+ String propName = getConfigurationPropertyName("supportAll");
+ allValue = this.getStringProperty(propName, ALL_OPTIONS_VALUE);
+ if (GenericUtils.isEmpty(allValue)) {
+ allValue = NO_OPTIONS_VALUE;
+ }
+
+ allSupportHolder.set(allValue);
+ return allValue;
+ }
+
+ @Override
+ public boolean isSecurityEntitySupported(Class<?> entityType, String name) {
+ if (!isSupported()) {
+ return false;
+ }
+
+ // Some known values it does not support
+ if (KeyPairGenerator.class.isAssignableFrom(entityType)
+ || KeyFactory.class.isAssignableFrom(entityType)) {
+ if (Objects.compare(name, SecurityUtils.EDDSA, String.CASE_INSENSITIVE_ORDER) == 0) {
+ return false;
+ }
+ } else if (Signature.class.isAssignableFrom(entityType)) {
+ if (Objects.compare(name, SecurityUtils.CURVE_ED25519_SHA512, String.CASE_INSENSITIVE_ORDER) == 0) {
+ return false;
+ }
+ }
+
+ return super.isSecurityEntitySupported(entityType, name);
+ }
+
+ @Override
+ public boolean isSupported() {
+ Boolean supported;
+ synchronized (supportHolder) {
+ supported = supportHolder.get();
+ if (supported != null) {
+ return supported.booleanValue();
+ }
+
+ ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(getClass());
+ supported = ReflectionUtils.isClassAvailable(cl, PROVIDER_CLASS);
+ supportHolder.set(supported);
+ }
+
+ return supported.booleanValue();
+ }
+}
[04/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-DSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-DSA-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-DSA-KeyPair
deleted file mode 100644
index 1d3ef24..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-DSA-KeyPair
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN DSA PRIVATE KEY-----
-MIIBvAIBAAKBgQDIPyMbBuQcZxeYDOyCqqkdK37cWQvp+RpWzvieB/oiG/ykfDQX
-oZMRtwqwWTBfejNitbBBmC6G/t5OK+9aFmj7pfJ+a7fZKXfiUquIg9soDsoOindf
-2AwR6MZ3os8uiP2xrC8IQAClnETa15mFShs4a4b2VjddgCQ6tphnY97MywIVAPtr
-YyW11RIXsVTf/9KlbhYaNlt5AoGAX9JzbHykC/0xDKOyKU6xDIOVdEZ0ooAl9Psl
-BEUuNhlv2XgmQScO6C9l2W7gbbut7zIw4FaZ2/dgXa3D4IyexBVug5XMnrssErZo
-NcoF5g0dgEAsb9Hl9gkIK3VHM5kWteeUg1VE700JTtSMisdL8CgIdR+xN8iVH5Ew
-CbLWxmECgYEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+ab
-YpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLa
-XWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7ACFQCE6flG
-nmVCAbzo9YsbdJWBnxMnBA==
------END DSA PRIVATE KEY-----
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-DSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-DSA-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-DSA-KeyPair.pub
deleted file mode 100644
index c0790ed..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-DSA-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-dss AAAAB3NzaC1kc3MAAACBAMg/IxsG5BxnF5gM7IKqqR0rftxZC+n5GlbO+J4H+iIb/KR8NBehkxG3CrBZMF96M2K1sEGYLob+3k4r71oWaPul8n5rt9kpd+JSq4iD2ygOyg6Kd1/YDBHoxneizy6I/bGsLwhAAKWcRNrXmYVKGzhrhvZWN12AJDq2mGdj3szLAAAAFQD7a2MltdUSF7FU3//SpW4WGjZbeQAAAIBf0nNsfKQL/TEMo7IpTrEMg5V0RnSigCX0+yUERS42GW/ZeCZBJw7oL2XZbuBtu63vMjDgVpnb92BdrcPgjJ7EFW6DlcyeuywStmg1ygXmDR2AQCxv0eX2CQgrdUczmRa155SDVUTvTQlO1IyKx0vwKAh1H7E3yJUfkTAJstbGYQAAAIEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+abYpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLaXWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7A= dsa-key-20130709
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-256-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-256-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-256-KeyPair
deleted file mode 100644
index 31b1268..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-256-KeyPair
+++ /dev/null
@@ -1,5 +0,0 @@
------BEGIN EC PRIVATE KEY-----
-MHcCAQEEIPKmiQzAASg656IP4PuuElLdLdO/MIXrGxQG6tGkKZ1HoAoGCCqGSM49
-AwEHoUQDQgAEobHtw9wkL332ep9fi8Gw5g8sEGwslNonPUCDR6YUZ9mjOehliLpF
-DLHLxlIFafrVM+LIpagjpRKZcnpGPWQDnA==
------END EC PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-256-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-256-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-256-KeyPair.pub
deleted file mode 100644
index 1c9763f..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-256-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKGx7cPcJC999nqfX4vBsOYPLBBsLJTaJz1Ag0emFGfZoznoZYi6RQyxy8ZSBWn61TPiyKWoI6USmXJ6Rj1kA5w= root@osv-linux
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-384-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-384-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-384-KeyPair
deleted file mode 100644
index 29af76f..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-384-KeyPair
+++ /dev/null
@@ -1,6 +0,0 @@
------BEGIN EC PRIVATE KEY-----
-MIGkAgEBBDB15z4n/vjug4fcEXPcgeonCHQuxJOwgFDIap/rgtM3EwuFDpE9wkfM
-K64UwV1ZSlygBwYFK4EEACKhZANiAARSJmbXE4/ONrLZXFRyxQRcUxMe5bt41vWm
-Qr3dK/X1DSmei20T4epdaCeKMwK58O163kAVHOaDXfRweUTSfI5dZ1l2OXFwQOzH
-Gayma2JpPs8TYR+lC/pDC2iZMp4CR0M=
------END EC PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-384-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-384-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-384-KeyPair.pub
deleted file mode 100644
index 93ceef0..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-384-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBFImZtcTj842stlcVHLFBFxTEx7lu3jW9aZCvd0r9fUNKZ6LbRPh6l1oJ4ozArnw7XreQBUc5oNd9HB5RNJ8jl1nWXY5cXBA7McZrKZrYmk+zxNhH6UL+kMLaJkyngJHQw== root@osv-linux
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-521-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-521-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-521-KeyPair
deleted file mode 100644
index 43f79d7..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-521-KeyPair
+++ /dev/null
@@ -1,7 +0,0 @@
------BEGIN EC PRIVATE KEY-----
-MIHcAgEBBEIBUacw+zn8Mw0PYaqqtAlOyaVXARegI6sK5YBhl5E1l9sqTzVN77ce
-1RrqQ8smfvZ6Hiw5gdGcPTszbiorVV5npg6gBwYFK4EEACOhgYkDgYYABACg4siC
-q1iqr4U/spXmw6b2VwBMsof7XLQGoD9wfwUikb8XWthNSmPP1nL6rlzJ5j8Bezn9
-BSSDfVAJfgqxmGIHdgHRVc0mkdq1/Q/DKhBgRyjZc29eo0o2ck3SNGNVaAabRYj6
-ck/iub/U6trKM7bdqy/joYYMwZdxLyYW5YxkPbqEfQ==
------END EC PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-521-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-521-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-521-KeyPair.pub
deleted file mode 100644
index 520b64e..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-EC-521-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACg4siCq1iqr4U/spXmw6b2VwBMsof7XLQGoD9wfwUikb8XWthNSmPP1nL6rlzJ5j8Bezn9BSSDfVAJfgqxmGIHdgHRVc0mkdq1/Q/DKhBgRyjZc29eo0o2ck3SNGNVaAabRYj6ck/iub/U6trKM7bdqy/joYYMwZdxLyYW5YxkPbqEfQ== root@osv-linux
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-RSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-RSA-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-RSA-KeyPair
deleted file mode 100644
index afc6aa8..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-RSA-KeyPair
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEoQIBAAKCAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2c
-tGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZB
-gIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlE
-aaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl
-5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwh
-V+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMwIBIwKCAQALW02YHN4OJz1Siypj
-xoNi87slUaBKBF/NlkWauGUIcpZFMTwnkIn6vCz5MhRbQC4oadRDzFNUrC/g7HdH
-prlqYe2P7uEGIfMb3YNFdk3tgOHmRsHqFgFMpVWsOjlTxNTUsQ74N3Isuxnha4wY
-9f90sBULc+WRdRvO9jbkSDaqoYVKAqCFWtocL+ZWwBXWrIrsQW4PElgZ/duc5DX7
-eeJ5DXCSj9dO+1KxsWEOKaoeABEegrRVys1/shcDNPhf/p0QShKIdPcpnDUc8cny
-1bq8GSt6jEQ+tuRoSnYrY+RD+mlkHrx373Xc1a9woV+QKTThmd9TQ8gzHMHNqq0a
-7kR7AoGBAOuPOTRiKaDtQyMTEX7eeHsPNE24EgvONjNpxyQ6gKGthG5SiB0IO7mP
-r7EggbR2EY8eMCY5HjvxzxgH86n2Pqbzsr6UlQq7YTPupCm/7fPgRknu917GA20f
-1cuY8B04Jp4FIGryBmCcScX6usXXhjfAvYCWWfkSytA8gX9+b1TNAoGBANf8shbp
-wRnQfgAzw2S+xs29pdwa6Jb/xuLvHSyklmgidrK4nsVI8G+zeCqwkqkNM02sM+vR
-c8EX7+myrGf+S2V3JS3AMNXEhavrWVH0CuqFHlBjSwHZ0uKuPpWHlCnud+23AdQz
-Bf1H7tYKt5es3J/B37o4YxhAL6U9qq+ewZH/AoGBAOTURjLjA94oT9jt870SoOyS
-bVLQEYfPol3UeE8UQnEsN4Ec+UDGK2PNaNfzsTL2WjNB5aF5UJIA184znD60seQC
-raMxQFORdF5ViYekgMEFwJ+XrnlSpD4e7PGqgtqOUWZOH33VKsRADSa5DTU3xDYo
-8porp9wDoHKD64MqXYWTAoGADFeVJeF4v6WDivukw+2k9dBSntz3WRXuG5iilNY0
-evqnsnDzITdeMkTFCcDycA9iBHA9ezCKRYxW2id3kOn1rgbO7KvliI7jESNkMJGa
-OUlvck7RFgxyc1pp+ep9fr0rbKtfMLJ1Xu4q56jXSn7oCSEFeFr+WSg9PKRwJ0rm
-fV8CgYAkYOiNY8jH5lwwoPWOIPJg62zdzmRQktrU3z7Nzk5udN6JnG3g57QjkozX
-AgHARKQ2MuXW9OfOnYNhbGeavcBQmg5XUx3eL4PRFw8mFZdjpBD/kM/dfCEwEta3
-FpRlVGn0RNqVV5xxClObD/CikkDqKZG4MSj3CrO3JK33gl1Lgg==
------END RSA PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-RSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-RSA-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-RSA-KeyPair.pub
deleted file mode 100644
index 9fac6f7..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/SecurityUtilsTest-RSA-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2ctGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZBgIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlEaaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwhV+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMw== lgoldstein@LGOLDSTEIN-WIN7
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-128-key
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-128-key b/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-128-key
deleted file mode 100644
index 2b93a42..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-128-key
+++ /dev/null
@@ -1,30 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: AES-128-CBC,D41AC063160FCC09B1E1931FB43BCEAA
-
-V218dGT2pUpimnwEVn+2ljvK6mvm2aNLlaakMunlHfIswrakJ1WTs8a61pYILOn9
-MGHrCiqe6ZI7FBJ2wXpSxhcuM3fzk6/dW4Ghh4EHG1Y94w97EizxNfyz/iI2XQw0
-i6ttaDLVzP8UcSRElqG+Zpe1A7EE/DkdkXD3f/DaGHtu1zirVeaEIggMLjfTdwnR
-sH9VnUZhe74VdPV0x16h7JjLt5fcbIjqJ6NWW4QvQpPBv3k0oiUy/nP4FXg1b7VW
-7SowuCPi+mF821hj4xSO8ETlAU1eZdtgXqtejtKm0iDtsjnTBZPvDDrq5ephBlxO
-k7JBJG1LFUiDIGnpxos5nCsKEo8UAw9a5/D4xE3C6UTocXon28XGzVCbkZBN6jcd
-UbpjCVwKMJmFL97487u9S57xrGTmJdi1AtF9Rei8juTTQY4+r3l2c7JtdtcbLUhj
-iLvdYnbh6kUEyE19/+omJaWGQlFhYp7ZMRRQSiz6TD8lhSIBPpXzs+uMfhkrifVk
-3WpjRoikmPOOFLtecee5Rp+SpGd700XgLnxwZ47l0FNfrKKqd3+nZX4JILQ2M0JP
-sBx8gcIew8aUqMzWrwZxbrt9Pd1+2kSNVG9hpLoNoA4WpQnYQMo4L0eTCeMNUOap
-f9H0Hh3QnqXTPHbcYZJCGE2RUxLzn/d7rUxUdEzER+pkhJcw9JbV/izTrpDHs9bM
-cfBLggQvs+UIBww2OFz2BztwoQzsSEuNW/SxG/y6SfRUQq5TZw9NxYnrrqfBXKtx
-svB1JVbn2fKq2Lvi8AZ1fF3tyrNot/tptDf0yDHejWDUvVx5cXsKVK2BbVjbZ88k
-mBtUbw7ea9Ev7ZsihNB2EdhPjLhhKlKLIZznPKeXL3GDTXqCgCxTVh4wLvaR8rDU
-C3Isil4WprCeynmZpOe7bxAZDm2QCobnDB8sLQqBI4zgH8X/1iyXJVdSKfK9vxcB
-sJ5pYCcS2q0C+CJkn6HVTlMQ5CyyzvPaDJukJoxwxsZ5hgCsUHFzrvyGnXqGfTBD
-qEW+oA7cj48CfweV5pXHj+mZpCrpn1zRVJRz4h1FZRsttPGtBRAlns5I3kh5BPRs
-4m1BO1jiWyp/7HkUrDRhEf/QeJsP+mTH32pQgnngZ/AGA0PUcKanMUpe1d2ju83V
-EIcTz9ycTHPiOAM6GaVt54fKj9WRBU+7pf14ZdJmfhp6twc0jNtaTh+/I6Pfb0jN
-0d6yKV//pOeJJBNhuOJgm/0vfkOnOojIJchOQCRt5Lg/a4fD/JXtLOed2zOQa+0J
-3d8Y93mQX/iN1wi95/sG79YBYF3FkJYVhjosSKbiIaxIn76zIx4IAlziycDKvgpr
-JgZcVvCDc4flwrf3Cv/uHK7UWOE+16X1CfAy8JzFg5bhiMmhgsJyHmd+zDGrY6NX
-zz+wLmwOenEwC40gpt89OXbgMcwJMtfiSusatRtZ+AAs0jb/8jExVXfcYE3m3r1/
-FqLZ7seTQT2D01YoPlwUtSPxzaZbziAJ/NaGmURnBGVibDCJxwUAiOSIQH4prIfg
-Q2FCJeMTbLV43Lanlby5nrmLkzsw3uo1MO8Of1DbcnVUHNSwrp/nNzrYdxBLIvqS
------END RSA PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-128-key.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-128-key.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-128-key.pub
deleted file mode 100644
index b1d66a6..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-128-key.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC/oDbs/yYxBdT02ldP6JIrcETJQ+TCml1tHYuo8cIQp0DZCgRZiEZ4foucAT8R/vLK01nnjRzrI42MXiCzyAHb1sPRD0Fsbpa4TFJczPBBRM2mp56airnArQUMmg/ZKlOf82hn+u7Kgn+ljyjYG5FrdoUBju62i0H4+oBfX+pTkd5ruUgqLyPUC3qtNLwjS4PIPAda/pfpsi9UawQ4ommWCCLlwK55NiSrPDBwKNuVWROcQps2NZRxzRLQEiiCEVBEdiUqqUQ+dg2beLV/4cCS860ZZRvCfe+ko8TUBJ7SLtcrvOEYJOKIZDVhcnQKN/wyXCHExSYytUconlFn/9YX root@devenv-aas
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-192-key
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-192-key b/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-192-key
deleted file mode 100644
index 014410d..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-192-key
+++ /dev/null
@@ -1,30 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: AES-192-CBC,EDF8E3E634D2106991113815A3C1F11F
-
-PZaaf2TCSCRmPZDnUa9Hq+RVGAXRXl8T8rduiqFLKG2Vk192AEo4WRPWhIcJ4ov+
-o6N69V0Fj/NrRyuwjihQQM27gTK2JK8cvDen+WB3mp5wmfFaazHXO21xNGMAp3bm
-obEeO+NHaG0080yxQ4AU2BzPZ1q/ePjDGBiENUsBAHeZvN2fM5uI4Cs9pqko2OM/
-ZiJQkn2QlnpSPm8B7kspqpEV9bn1peVYAMIkp/yES18Kw50kan3R2CqccutVQfdP
-8ddTPPUvWUrDSrW/Ae2qZvRoCXVLDhy1jBKQDMUCrxqDJCo58GR0FWxK4nhCgfeL
-wmxGaKL/3uKDl5otkj93C1qCbEvQCJm5rcwEjkRVfeSPvNNh+WQrJE7uNcJB4kXX
-tF8c5peZI8ymdlFFXlCqjVpdhJh0YKRhpxhWKLA15I1bbwwY4/mwIIMyXOcAQo58
-G7XOwBPw01/lSJ/YTR8y1ubFbWJ6LgoLgSzsOLnBxJJji148UiNDPPbal0FN8vK2
-VsSRBrjrNHSmh+BSA0AStq4QtQpwNMU0qKFLzx5HvoYiaSG+OKtJLZNqTGzDG5aS
-Xe8rNoTQTaZBy5yTY1zTn2lMFngWSt/aYyXfc1BXdYyTOk/eoGHtXxp91nQtCdkG
-4VMMfPlnbW1Q2Cq4ATdcIUPPKLaNVlP2TbloBb1wYjXy8RfFFqZVNOLnxXnXOXUG
-U5SlG44XV5AouwhrlUPrm+0+9jIh4pO4sfG0ldGwabGbUMZ6vvgcU9w1yRcsjycG
-v9yH7CfFKKXjUL5RR2weZVD8vRjQMbfVHRt3P4NFClI6e1gXx92FmT7x97866e+T
-TohyC7uZQq2prBzt8BwxFP0h19+CiTVzofe2I9x+Hce67F+ATnLq9fxOnjZlwK1i
-K95eK+Itc9zSu9tIZGaLvghOExZ24893Ncz5NJ2nn0YptxSi24e8PcECHhSUrjv9
-1VOi8JUmv58bQ5ocuUlHmB2FkpnsYUNjtQx1OT2IRovQJIDCttHhT3QIGoiqoeTw
-sSwyz0gDYzAJtuVkSZxqXJ4LEMozaA3uwxv0TfaQordm+GuGKuV5MsKyEQtITUk5
-5bgQfS0VQqkbq2fRjtd3nIJWCF5otrVulY0tzoTBjGZ2trNj5nz5ZEUTFdRSAaxb
-F+i3Mv8gGPC+2YIo8feDCHnRQ1AALjLon8nKefRsWAOYUozNCtHvu4g7WK6+GcPd
-eiYM7tbSrNCHg+KdB+eWrJCfVCgVHTj8mGb3EiH6oxgx51TmlyuQYJAVseWjQOdm
-IDKiAsNNZijmgMWLsFZG1VvF/446VWIGkSW9eB6bB7aR1Kg4F18VkXdn+SPmaHrC
-NHhNa9dOmcOvlNXIs7jlTjRVEC0pp8DH0AUdPjqlR/F1IHb7uFyqlza8Cqb7PZJs
-x+9GbSwuyKLKLvMzmsnBtpRT32Srsbx6pWst40gaoOMFxto22vmGxz4IABGi96V/
-NrrDHtuZ5OaePl7GihKcUCSSVcoFO2jKKNMMHSSVGhxkfir7qGzMe5LZ9/zl+bvW
-vBS0wSPUd+P3mLc0m53mH+g26I479ryX6/2W+GecB1h5p7S7vJMPQjqZAJskogf5
------END RSA PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-192-key.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-192-key.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-192-key.pub
deleted file mode 100644
index 3fda036..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-192-key.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCutvykV6G6t4JlLhlJOQKknSB++PXLTtuSjXaH77hUUEl/yc9VbEsfOz1NySBRQDWT0sgRZ1D2YAxaIvK9+0wZPnRNO1ptDrwj650Gflh8vKkEQOlHH7ISTxQTHtyHGeXfm3Bl+qqxTMPG5gcxxkKINMvKAkNYBnOyVX1OXUvqtM1jx6THlfWPQ9yFuVmZDVj1W5VItxjG293RejbjN1EjhhGRBiqwczcIPgPiMDfR0+xFNEQt6YD4k9TFEkLmIfCn+fcqKJhP/z99bRosMHtkRwfX/Rco+G9dRwe1sHvVH7ETeS7v3vl3WoM58AlpYYGUIXsxozRdGE93AmTkaSIj SecurityUtilsTest
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-256-key
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-256-key b/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-256-key
deleted file mode 100644
index 26b699e..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-256-key
+++ /dev/null
@@ -1,30 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: AES-256-CBC,4FF564F3E05187BEDEDA0529D44EE13E
-
-z7KGBVkxJzNS1Z8vMurcYmHhlbxnUYHry87VgzV38UkwsLSo5FIPNUaxvLPeD3Im
-YW4hQ/1Z+3onSwTgaUqxih6YK+fHS0RZD5R4HjOWaERCPZa5/oDciLrjOU3EKkSV
-n84/ezqUn8GlMJYcpB2sURCmnJZ6erHCVSPAhVlsrWPO9uZHx8BpaclqsvC4Gq7a
-C4liGjDNsFvVO998eDIDLmHeO+qGnyeQK+pu3/+Ausvd9QjUf4L3T+Lu+Cgr0w59
-tB3TxeGxPel9Av88VYIhCddIEvRyMcN8lzLrrYrsLNPMmaF1x2uNDORJCTlEjvLw
-QG6zugofiUVdZdsWRPFmM8DbujeoUHkPkwImXedhiKz1Tp0Rz6gqGx6tzsqEe0kD
-eS2e0Ibj41fFxpkQlaG/xDliUvntkoQ6v7ZKbXQrsBP4hyoMcjeUMhWL+5shkJEV
-RzvZOl7WKoovFnf3glLdiDFqlfLK5BhBJckLmdmnVvsD1HYeKqSDrx+r3hUF3yZ5
-Obxxk++QTC1qHhnyAwqW7Y2owq81pmOhgxKqh/4OoxYV0trIgzI47+5hKsp12Van
-7qBCfTtXv4IlBKaKARTroQ2sDPAi1B2j8mU5lrGm81BzsLSqh1IJjOMsccWNfjMS
-jQMKvcQN94y7AvxcBDGVn9FZftNWs9/ZwitRCFfrz/n0u6hLDQBbH6muDffo4ZhF
-ZiH23W743N/ZASC/kA7LZ3pmH6TNl2Zf1gP+tTrfXu17BRDJYi8VqHBcuck/M4Ox
-qOpP4mK2B/aAVmTkty67+QJerIwsW+h8LsjkQTnXGoneobsx2nL8OvJtPHkOUo4b
-1zFyFvQK+V8DWWAml4Y8V+4NCgrybqqK11ApRl7FfaOyCMgZ+AvqzjK3E5s1gsaf
-AaxORV0iDE0Y8nhq9EEhDNTr5Yi6FeHuRmVsack1AQqYn1U64k21x4ZEmDjslq9t
-WKizqG6jz6xtRtsypr0oCBYirt8p5qDmeO58MdES6A+nkOZC0ysyhizpDWdj01rQ
-6w3el0t/wUh1lBC3UCTeD7Vz20B1rAs5AlbdFmOUGHoH2Hjty1DQVZ+Fl5/K9AQi
-yaVR07IDE45i9rvl7DThRHT6Qd0KAb7bIo+Xlz/xKUgA0UVLkpfGtgDNi1DSTsBQ
-m6xlvR9M3tmMwnHlOEfX8YAGIQR9JLiImUtypjXnp6IdxCn682UIWC9XakqMFbzS
-Hm4Y0VBHYPisByl7PuavEP69pgYVdt1PqFFOc3xL42QVihYsM9huw1j4rlP7/jXB
-zJyH6yuqZHV7BYmurBByxRCe4joN24D6HYWTLKhPjgIG++n/IZ+PO7s0khrTh3Pl
-L3bOujyLtr88w6PPhBOCVDPgmJEC6lIGC8zhh+S9Knewr50PsvOoQVKGDTSPO+G7
-NNa/ugN9/eTeO2ru2y6HuahWrf2seKrTQB8JVYP0SZkOe4zVdMRR5M559iLIdh0e
-U/hrj6v/dYVbIkmSWdhtxx0jXbM2VlWTeQQjUYzeVumVhZ1pk2P0LeHrSWhHa5h2
-hadw69Ht0hRTbljZ1MHjK9BbQI1Ph+YBuDr0aPVBf2ePYFvPObw2Mik7ctiDlmQT
------END RSA PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-256-key.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-256-key.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-256-key.pub
deleted file mode 100644
index 6a3a0ac..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-AES-256-key.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNUSajd7RzgqvQ2Q2em3CFA4Fh2f8YJkzJTPrcn9L9SFZnent2rABlVVr216jh3hAjhRFEkRhsoWUxUB7UYN6Ox/hGXrfCtD9wB0QbpOjNb0tnSovl3e+LfnWO5mpaRAb9croiuKVwQXuiwLNOQqs5C2wvLJhc77U4myuvFycYQ9Gh9g12g05Q8Qs+Du6hFSV1n1ZDMTtiVYknhe2gCLmBh1ghpaGeG3eWpd8EXumTrpwrylb1hJtj4yo748dXFa/YUJpqzCNyoA+0aY+v0MwgxZcS6zWt5VQ3yqRi4YrXX7vvhz80rKmJQlEhvM9rVKAwKc/qkJdTWlU+v0+/zY7/ hello@world
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-DES-EDE3-key
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-DES-EDE3-key b/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-DES-EDE3-key
deleted file mode 100644
index e6f6156..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-DES-EDE3-key
+++ /dev/null
@@ -1,30 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: DES-EDE3-CBC,7DD956370CB0B83E
-
-oTBNWVUGIKQ3LX/bIE+upuTtiVSWQ9ooTxdNJRIpGsq6oTevtxgkuIF9oO+ShZJq
-zf/mTQPtAyRHSX8JsTKw9GZhd5w7c07V3wfzx689RHFCZDW1DIl67KvwyhRwfRtY
-HXZGYPW/XmOgh3Gw31csmLlJd42r8d+7MRIfmo25SO5Sy67mNccRJikdbYa/HH50
-yMVaf7o73tLLIS3WNnhaKjf9hRsSW370aICk3hfS/NMriae0xAMpqpYQmGkn9Zhh
-zTg+Kjo0FiEZEnMNKZQRRWx7VsoQRfrebOZq3LF7tfEnJcKneJ+grvQp0FpkEMzN
-JeiaEwtoIrXRU5KENQbEnXqF2Mpai6ejh5lVflcHqI01lwWGWkwxnNpxgMbWCKuB
-Unad3fQQLFQU67B8jQpES2AQVXO7UaaDHNtATftC2gFpBktlkSurT/KZBT4bVfS7
-z3K9gFB/lYrji2ef+xuzbLmq+eja9OlZIVxH7RTxODkyrxeNhtEUXJgezOB64Cny
-YFrsDPcR8rSRc5SjMMuAy2dSkyndSp3m2YmtVFHibPrX18K2lblRkiuVDci0EX4p
-1XzaCJ9mhdtMedpIzFdwgCOt/M8OBw9g02qgOK1uBu37tXdACNxhX/wrFPCtbJjD
-yBwnBbXoeca8ZF45uw1egKlHaY2d7kTsH+bdx04bTo4Xq0+lXMALxxkfwTOt27xL
-JQp15AFm6ZAshVt7hxCtm512HoSkkFYMPUPawH9HVZE6f4sbNBlbqgCmYhD5HPMN
-Kd3ssQWTXwiOSd+k8HuBUBhtdZsgdpSdR5xH9wEymS9fcGmrf2yLuRlxhvoqo44X
-+go5CqLoAKoUtVJdtZrkmMdF1OxufDBZ16+fKN8NNE8vQv8Kt221yg/mmDy3t6mh
-axynBSxk1Qtjz2H9iZJ5nglF2Hceb1FQXHGlLCNv+cwA9vRC0pQJ25fPHVhvxhmW
-gnLJ5OY+zXENUWvMIeAGCSfjSVtnSVWAfdJ7kgHZc/wCWO6rYAwlfsPDq8ZdQ8Za
-4Hm6sUnkHHtagvJZahjdSPoziE68FiHqFU3PdmuQsZpCFOzloxahWImgx0UM9n8k
-2DlRPK6bMBCjpUrb5D0jp8YU2KDepw2D4O7RaFLh47w78oxrO1gABY23M4JQfUiP
-lzsfsGy9OI1twCcsJhFOX95nL9w+hlvFALTkY5UT3dB3eWP3k2nE12UxQhE3hVZ9
-vI100y2m/FUnKkeGYkvhRw9yD611jjYuRYGL3LsKNaOK97Ses+vAB0LNOss+12vz
-7bYvDUVJ5csfpPGqxyzYwIbytDwUcvxTLxRlgQNb2YMqzSmVodO0lyXdNF1/KUFF
-nIdlh0KSe4BALzYhya9V71MngLHFifp/erS4gdnFbbBCmZTEZhziBnqimRPoN5Co
-K+22blcRkDytiL+NTRybvfOYvy6Ulb4OK/So+Y5r1B5KGhJqFWSeo0g2ftAB6r1h
-HSbfoxYO/xCMQy7aNXuJI0giVRThcQsQapTO2DMeymt/0nj8qEPF0dubtbmgJ4dd
-aZuYaY1Qxk0umVfq9zHsRTbRb+16TuZtp+7brDDRpRe6lSzdbTNShg==
------END RSA PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-DES-EDE3-key.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-DES-EDE3-key.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-DES-EDE3-key.pub
deleted file mode 100644
index f5c8c85..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/super-secret-passphrase-RSA-DES-EDE3-key.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxvz5QWkCxznYbBOUbMFsCf+PJLauyZTcPsHLPZ/lHYH7EIa9s8NWbfFyTJXVwcuJiDCoRZ8d7KBahAuNjGeXsuJQuCqlLLl6GKoFnI0hnbKbFHDDSub+s3WvLdaTeF22qlTmlPb3RFYUO0cWk9MVpVNM6ev33CUGv4Dmbr0dZNkgUOkcPHiQpxKizsED+EWSVh1Ptx8AEDObNyFHJljbHUeJrHTIlcaekJheRXQWLvsJqKD0TN+Dkvi044MDWG9VjVyNsyXBCz1Vk9VaK1dNkkH+RDGTsFvFj7IPwBS/FliEVRrKOUgjMrmKUPbAm8IblIsno+HLZ60OB6X6hu2iHw== lgoldstein@LGOLDSTEIN-WIN7
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-git/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-git/pom.xml b/sshd-git/pom.xml
index b31f28e..8a4205f 100644
--- a/sshd-git/pom.xml
+++ b/sshd-git/pom.xml
@@ -62,6 +62,13 @@
</dependency>
<dependency>
<groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
<artifactId>sshd-sftp</artifactId>
<version>${project.version}</version>
<scope>test</scope>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java b/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
index 77e520a..0764e91 100644
--- a/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
+++ b/sshd-git/src/test/java/org/apache/sshd/git/pack/GitPackCommandTest.java
@@ -30,8 +30,8 @@ import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.auth.password.AcceptAllPasswordAuthenticator;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.apache.sshd.util.test.JSchLogger;
-import org.apache.sshd.util.test.Utils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.transport.CredentialsProvider;
@@ -79,14 +79,14 @@ public class GitPackCommandTest extends BaseTestSupport {
int port = sshd.getPort();
try {
Path serverDir = serverRootDir.resolve(getCurrentTestName() + Constants.DOT_GIT_EXT);
- Utils.deleteRecursive(serverDir);
+ CommonTestSupportUtils.deleteRecursive(serverDir);
Git.init().setBare(true).setDirectory(serverDir.toFile()).call();
JSch.setConfig("StrictHostKeyChecking", "no");
CredentialsProvider.setDefault(new UsernamePasswordCredentialsProvider(getCurrentTestName(), getCurrentTestName()));
Path localRootDir = gitRootDir.resolve("local");
Path localDir = localRootDir.resolve(serverDir.getFileName());
- Utils.deleteRecursive(localDir);
+ CommonTestSupportUtils.deleteRecursive(localDir);
SshClient client = SshClient.setUpDefaultClient();
SshSessionFactory.setInstance(new GitSshdSessionFactory(client));
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
----------------------------------------------------------------------
diff --git a/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java b/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
index 1e5973d..18cf599 100644
--- a/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
+++ b/sshd-git/src/test/java/org/apache/sshd/git/pgm/GitPgmCommandTest.java
@@ -34,7 +34,7 @@ import org.apache.sshd.git.GitLocationResolver;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.eclipse.jgit.api.Git;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -58,7 +58,7 @@ public class GitPgmCommandTest extends BaseTestSupport {
int port = sshd.getPort();
try {
- Utils.deleteRecursive(serverDir);
+ CommonTestSupportUtils.deleteRecursive(serverDir);
try (SshClient client = setupTestClient()) {
client.start();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-ldap/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-ldap/pom.xml b/sshd-ldap/pom.xml
index 3415c1f..9f6a9ba 100644
--- a/sshd-ldap/pom.xml
+++ b/sshd-ldap/pom.xml
@@ -102,6 +102,13 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-ldap/src/test/java/org/apache/sshd/server/auth/BaseAuthenticatorTest.java
----------------------------------------------------------------------
diff --git a/sshd-ldap/src/test/java/org/apache/sshd/server/auth/BaseAuthenticatorTest.java b/sshd-ldap/src/test/java/org/apache/sshd/server/auth/BaseAuthenticatorTest.java
index a614869..d4881ca 100644
--- a/sshd-ldap/src/test/java/org/apache/sshd/server/auth/BaseAuthenticatorTest.java
+++ b/sshd-ldap/src/test/java/org/apache/sshd/server/auth/BaseAuthenticatorTest.java
@@ -55,8 +55,8 @@ import org.apache.directory.shared.ldap.schema.registries.SchemaLoader;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.apache.sshd.util.test.NoIoTestCase;
-import org.apache.sshd.util.test.Utils;
import org.junit.experimental.categories.Category;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -103,8 +103,9 @@ public abstract class BaseAuthenticatorTest extends BaseTestSupport {
@SuppressWarnings("checkstyle:avoidnestedblocks")
public static SimpleImmutableEntry<LdapServer, DirectoryService> startApacheDs(Class<?> anchor) throws Exception {
Logger log = LoggerFactory.getLogger(anchor);
- File targetFolder = Objects.requireNonNull(Utils.detectTargetFolder(anchor), "Failed to detect target folder");
- File workingDirectory = assertHierarchyTargetFolderExists(Utils.deleteRecursive(Utils.resolve(targetFolder, anchor.getSimpleName(), "apacheds-work")));
+ File targetFolder = Objects.requireNonNull(CommonTestSupportUtils.detectTargetFolder(anchor), "Failed to detect target folder");
+ File anchorFolder = CommonTestSupportUtils.resolve(targetFolder, anchor.getSimpleName(), "apacheds-work");
+ File workingDirectory = assertHierarchyTargetFolderExists(CommonTestSupportUtils.deleteRecursive(anchorFolder));
DirectoryService directoryService = new DefaultDirectoryService();
directoryService.setWorkingDirectory(workingDirectory);
@@ -140,7 +141,9 @@ public abstract class BaseAuthenticatorTest extends BaseTestSupport {
{
JdbmPartition systemPartition = new JdbmPartition();
systemPartition.setId("system");
- systemPartition.setPartitionDir(assertHierarchyTargetFolderExists(Utils.deleteRecursive(new File(workingDirectory, systemPartition.getId()))));
+
+ File partitionFolder = CommonTestSupportUtils.deleteRecursive(new File(workingDirectory, systemPartition.getId()));
+ systemPartition.setPartitionDir(assertHierarchyTargetFolderExists(partitionFolder));
systemPartition.setSuffix(ServerDNConstants.SYSTEM_DN);
systemPartition.setSchemaManager(schemaManager);
directoryService.setSystemPartition(systemPartition);
@@ -151,7 +154,9 @@ public abstract class BaseAuthenticatorTest extends BaseTestSupport {
JdbmPartition partition = new JdbmPartition();
partition.setId("users");
partition.setSuffix(BASE_DN_TEST);
- partition.setPartitionDir(assertHierarchyTargetFolderExists(Utils.deleteRecursive(new File(workingDirectory, partition.getId()))));
+
+ File partitionFolder = CommonTestSupportUtils.deleteRecursive(new File(workingDirectory, partition.getId()));
+ partition.setPartitionDir(assertHierarchyTargetFolderExists(partitionFolder));
directoryService.addPartition(partition);
}
@@ -251,7 +256,7 @@ public abstract class BaseAuthenticatorTest extends BaseTestSupport {
log.info("Directory service shut down");
log.info("Deleting " + workDir.getAbsolutePath());
- Utils.deleteRecursive(workDir);
+ CommonTestSupportUtils.deleteRecursive(workDir);
log.info(workDir.getAbsolutePath() + " deleted");
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-mina/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-mina/pom.xml b/sshd-mina/pom.xml
index 14f37a5..5defe91 100644
--- a/sshd-mina/pom.xml
+++ b/sshd-mina/pom.xml
@@ -57,6 +57,13 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>net.i2p.crypto</groupId>
<artifactId>eddsa</artifactId>
<scope>test</scope>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-netty/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-netty/pom.xml b/sshd-netty/pom.xml
index 5dcdb2e..3482db4 100644
--- a/sshd-netty/pom.xml
+++ b/sshd-netty/pom.xml
@@ -64,6 +64,13 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>net.i2p.crypto</groupId>
<artifactId>eddsa</artifactId>
<scope>test</scope>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-scp/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-scp/pom.xml b/sshd-scp/pom.xml
index ac02bd9..f6eae7d 100644
--- a/sshd-scp/pom.xml
+++ b/sshd-scp/pom.xml
@@ -42,7 +42,7 @@
<artifactId>sshd-core</artifactId>
<version>${project.version}</version>
</dependency>
-
+ <!-- Test dependencies -->
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
@@ -51,6 +51,13 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-scp/src/test/java/org/apache/sshd/client/scp/ScpTest.java
----------------------------------------------------------------------
diff --git a/sshd-scp/src/test/java/org/apache/sshd/client/scp/ScpTest.java b/sshd-scp/src/test/java/org/apache/sshd/client/scp/ScpTest.java
index 9d81194..21ff1a4 100644
--- a/sshd-scp/src/test/java/org/apache/sshd/client/scp/ScpTest.java
+++ b/sshd-scp/src/test/java/org/apache/sshd/client/scp/ScpTest.java
@@ -61,9 +61,10 @@ import org.apache.sshd.server.command.Command;
import org.apache.sshd.server.scp.ScpCommand;
import org.apache.sshd.server.scp.ScpCommandFactory;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.apache.sshd.util.test.JSchLogger;
import org.apache.sshd.util.test.SimpleUserInfo;
-import org.apache.sshd.util.test.Utils;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -143,12 +144,12 @@ public class ScpTest extends BaseTestSupport {
@BeforeClass
public static void setupClientAndServer() throws Exception {
JSchLogger.init();
- sshd = Utils.setupTestServer(ScpTest.class);
+ sshd = CoreTestSupportUtils.setupTestServer(ScpTest.class);
sshd.setCommandFactory(new ScpCommandFactory());
sshd.start();
port = sshd.getPort();
- client = Utils.setupTestClient(ScpTest.class);
+ client = CoreTestSupportUtils.setupTestClient(ScpTest.class);
client.start();
}
@@ -180,17 +181,18 @@ public class ScpTest extends BaseTestSupport {
public void testNormalizedScpRemotePaths() throws Exception {
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(scpRoot);
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(scpRoot);
Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
Path localFile = localDir.resolve("file.txt");
- byte[] data = Utils.writeFile(localFile, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
+ byte[] data = CommonTestSupportUtils.writeFile(localFile, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
Path remoteFile = remoteDir.resolve(localFile.getFileName().toString());
String localPath = localFile.toString();
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
String[] remoteComps = GenericUtils.split(remotePath, '/');
Factory<? extends Random> factory = client.getRandomFactory();
Random rnd = factory.create();
@@ -240,17 +242,18 @@ public class ScpTest extends BaseTestSupport {
public void testUploadAbsoluteDriveLetter() throws Exception {
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(scpRoot);
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(scpRoot);
Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
Path localFile = localDir.resolve("file-1.txt");
- byte[] data = Utils.writeFile(localFile, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
+ byte[] data = CommonTestSupportUtils.writeFile(localFile, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
Path remoteFile = remoteDir.resolve(localFile.getFileName().toString());
String localPath = localFile.toString();
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
.verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.getSession()) {
@@ -262,12 +265,12 @@ public class ScpTest extends BaseTestSupport {
assertFileLength(remoteFile, data.length, TimeUnit.SECONDS.toMillis(5L));
Path secondRemote = remoteDir.resolve("file-2.txt");
- String secondPath = Utils.resolveRelativeRemotePath(parentPath, secondRemote);
+ String secondPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, secondRemote);
scp.upload(localPath, secondPath);
assertFileLength(secondRemote, data.length, TimeUnit.SECONDS.toMillis(5L));
Path pathRemote = remoteDir.resolve("file-path.txt");
- String pathPath = Utils.resolveRelativeRemotePath(parentPath, pathRemote);
+ String pathPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, pathRemote);
scp.upload(localFile, pathPath);
assertFileLength(pathRemote, data.length, TimeUnit.SECONDS.toMillis(5L));
}
@@ -286,18 +289,19 @@ public class ScpTest extends BaseTestSupport {
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(scpRoot);
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(scpRoot);
Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
Path localFile = localDir.resolve("file.txt");
- Utils.writeFile(localFile, data);
+ CommonTestSupportUtils.writeFile(localFile, data);
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
Path remoteFile = remoteDir.resolve(localFile.getFileName());
- Utils.writeFile(remoteFile, data + data);
+ CommonTestSupportUtils.writeFile(remoteFile, data + data);
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
scp.upload(localFile.toString(), remotePath);
assertFileLength(remoteFile, data.length(), TimeUnit.SECONDS.toMillis(5L));
}
@@ -306,7 +310,8 @@ public class ScpTest extends BaseTestSupport {
@Test
public void testScpUploadZeroLengthFile() throws Exception {
Path targetPath = detectTargetFolder();
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
Path zeroLocal = localDir.resolve("zero.txt");
@@ -330,7 +335,7 @@ public class ScpTest extends BaseTestSupport {
session.auth().verify(AUTH_TIMEOUT, TimeUnit.SECONDS);
ScpClient scp = createScpClient(session);
- String remotePath = Utils.resolveRelativeRemotePath(targetPath.getParent(), zeroRemote);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(targetPath.getParent(), zeroRemote);
scp.upload(zeroLocal.toString(), remotePath);
assertFileLength(zeroRemote, 0L, TimeUnit.SECONDS.toMillis(5L));
}
@@ -339,7 +344,8 @@ public class ScpTest extends BaseTestSupport {
@Test
public void testScpDownloadZeroLengthFile() throws Exception {
Path targetPath = detectTargetFolder();
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
Path zeroLocal = localDir.resolve(getCurrentTestName());
@@ -362,7 +368,7 @@ public class ScpTest extends BaseTestSupport {
session.auth().verify(AUTH_TIMEOUT, TimeUnit.SECONDS);
ScpClient scp = createScpClient(session);
- String remotePath = Utils.resolveRelativeRemotePath(targetPath.getParent(), zeroRemote);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(targetPath.getParent(), zeroRemote);
scp.download(remotePath, zeroLocal.toString());
assertFileLength(zeroLocal, 0L, TimeUnit.SECONDS.toMillis(5L));
}
@@ -375,8 +381,9 @@ public class ScpTest extends BaseTestSupport {
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(scpRoot);
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(scpRoot);
Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
Path localOutFile = localDir.resolve("file-1.txt");
@@ -390,12 +397,12 @@ public class ScpTest extends BaseTestSupport {
session.auth().verify(AUTH_TIMEOUT, TimeUnit.SECONDS);
ScpClient scp = createScpClient(session);
- Utils.writeFile(localOutFile, data);
+ CommonTestSupportUtils.writeFile(localOutFile, data);
assertFalse("Remote folder already exists: " + remoteDir, Files.exists(remoteDir));
String localOutPath = localOutFile.toString();
- String remoteOutPath = Utils.resolveRelativeRemotePath(parentPath, remoteOutFile);
+ String remoteOutPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteOutFile);
outputDebugMessage("Expect upload failure %s => %s", localOutPath, remoteOutPath);
try {
scp.upload(localOutPath, remoteOutPath);
@@ -410,13 +417,13 @@ public class ScpTest extends BaseTestSupport {
assertFileLength(remoteOutFile, data.length(), TimeUnit.SECONDS.toMillis(11L));
Path secondLocal = localDir.resolve(localOutFile.getFileName());
- String downloadTarget = Utils.resolveRelativeRemotePath(parentPath, secondLocal);
+ String downloadTarget = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, secondLocal);
outputDebugMessage("Expect download success %s => %s", remoteOutPath, downloadTarget);
scp.download(remoteOutPath, downloadTarget);
assertFileLength(secondLocal, data.length(), TimeUnit.SECONDS.toMillis(11L));
Path localPath = localDir.resolve("file-path.txt");
- downloadTarget = Utils.resolveRelativeRemotePath(parentPath, localPath);
+ downloadTarget = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, localPath);
outputDebugMessage("Expect download success %s => %s", remoteOutPath, downloadTarget);
scp.download(remoteOutPath, downloadTarget);
assertFileLength(localPath, data.length(), TimeUnit.SECONDS.toMillis(11L));
@@ -434,19 +441,20 @@ public class ScpTest extends BaseTestSupport {
ScpClient scp = createScpClient(session);
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(scpRoot);
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(scpRoot);
Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
Path local1 = localDir.resolve("file-1.txt");
- byte[] data = Utils.writeFile(local1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
+ byte[] data = CommonTestSupportUtils.writeFile(local1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
Path local2 = localDir.resolve("file-2.txt");
Files.write(local2, data);
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
Path remote1 = remoteDir.resolve(local1.getFileName());
- String remote1Path = Utils.resolveRelativeRemotePath(parentPath, remote1);
+ String remote1Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remote1);
String[] locals = {local1.toString(), local2.toString()};
try {
scp.upload(locals, remote1Path);
@@ -464,7 +472,7 @@ public class ScpTest extends BaseTestSupport {
}
Path remoteSubDir = assertHierarchyTargetFolderExists(remoteDir.resolve("dir"));
- scp.upload(locals, Utils.resolveRelativeRemotePath(parentPath, remoteSubDir));
+ scp.upload(locals, CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteSubDir));
Path remoteSub1 = remoteSubDir.resolve(local1.getFileName());
assertFileLength(remoteSub1, data.length, TimeUnit.SECONDS.toMillis(11L));
@@ -473,12 +481,12 @@ public class ScpTest extends BaseTestSupport {
assertFileLength(remoteSub2, data.length, TimeUnit.SECONDS.toMillis(11L));
String[] remotes = {
- Utils.resolveRelativeRemotePath(parentPath, remoteSub1),
- Utils.resolveRelativeRemotePath(parentPath, remoteSub2),
+ CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteSub1),
+ CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteSub2),
};
try {
- scp.download(remotes, Utils.resolveRelativeRemotePath(parentPath, local1));
+ scp.download(remotes, CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, local1));
fail("Unexpected download success to existing local file: " + local1);
} catch (IOException e) {
// Ok
@@ -511,26 +519,27 @@ public class ScpTest extends BaseTestSupport {
ScpClient scp = createScpClient(session);
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(scpRoot);
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(scpRoot);
Path localDir = scpRoot.resolve("local");
Path localSubDir = assertHierarchyTargetFolderExists(localDir.resolve("dir"));
Path localSub1 = localSubDir.resolve("file-1.txt");
- byte[] data = Utils.writeFile(localSub1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
+ byte[] data = CommonTestSupportUtils.writeFile(localSub1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
Path localSub2 = localSubDir.resolve("file-2.txt");
Files.write(localSub2, data);
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
- scp.upload(localSubDir, Utils.resolveRelativeRemotePath(parentPath, remoteDir), ScpClient.Option.Recursive);
+ scp.upload(localSubDir, CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteDir), ScpClient.Option.Recursive);
Path remoteSubDir = remoteDir.resolve(localSubDir.getFileName());
assertFileLength(remoteSubDir.resolve(localSub1.getFileName()), data.length, TimeUnit.SECONDS.toMillis(11L));
assertFileLength(remoteSubDir.resolve(localSub2.getFileName()), data.length, TimeUnit.SECONDS.toMillis(11L));
- Utils.deleteRecursive(localSubDir);
+ CommonTestSupportUtils.deleteRecursive(localSubDir);
- scp.download(Utils.resolveRelativeRemotePath(parentPath, remoteSubDir), localDir, ScpClient.Option.Recursive);
+ scp.download(CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteSubDir), localDir, ScpClient.Option.Recursive);
assertFileLength(localSub1, data.length, TimeUnit.SECONDS.toMillis(11L));
assertFileLength(localSub2, data.length, TimeUnit.SECONDS.toMillis(11L));
}
@@ -547,17 +556,18 @@ public class ScpTest extends BaseTestSupport {
ScpClient scp = createScpClient(session);
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(scpRoot);
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(scpRoot);
Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
Path local1 = localDir.resolve("file-1.txt");
- byte[] data = Utils.writeFile(local1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
+ byte[] data = CommonTestSupportUtils.writeFile(local1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
Path local2 = localDir.resolve("file-2.txt");
Files.write(local2, data);
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteDir);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteDir);
scp.upload(localDir.toString() + File.separator + "*", remotePath);
assertFileLength(remoteDir.resolve(local1.getFileName()), data.length, TimeUnit.SECONDS.toMillis(11L));
assertFileLength(remoteDir.resolve(local2.getFileName()), data.length, TimeUnit.SECONDS.toMillis(11L));
@@ -581,18 +591,19 @@ public class ScpTest extends BaseTestSupport {
ScpClient scp = createScpClient(session);
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(scpRoot);
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(scpRoot);
Path localDir = scpRoot.resolve("local");
Path localSubDir = assertHierarchyTargetFolderExists(localDir.resolve("dir"));
Path local1 = localDir.resolve("file-1.txt");
- byte[] data = Utils.writeFile(local1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
+ byte[] data = CommonTestSupportUtils.writeFile(local1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
Path localSub2 = localSubDir.resolve("file-2.txt");
Files.write(localSub2, data);
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteDir);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteDir);
scp.upload(localDir.toString() + File.separator + "*", remotePath, ScpClient.Option.Recursive);
assertFileLength(remoteDir.resolve(local1.getFileName()), data.length, TimeUnit.SECONDS.toMillis(11L));
@@ -600,7 +611,7 @@ public class ScpTest extends BaseTestSupport {
assertFileLength(remoteSubDir.resolve(localSub2.getFileName()), data.length, TimeUnit.SECONDS.toMillis(11L));
Files.delete(local1);
- Utils.deleteRecursive(localSubDir);
+ CommonTestSupportUtils.deleteRecursive(localSubDir);
scp.download(remotePath + "/*", localDir);
assertFileLength(local1, data.length, TimeUnit.SECONDS.toMillis(11L));
@@ -624,8 +635,9 @@ public class ScpTest extends BaseTestSupport {
ScpClient scp = createScpClient(session);
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(scpRoot);
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(scpRoot);
Path localDir = scpRoot.resolve("local");
Path localSubDir = assertHierarchyTargetFolderExists(localDir.resolve("dir"));
@@ -633,7 +645,7 @@ public class ScpTest extends BaseTestSupport {
final long lastModMillis = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1);
final long lastModSecs = TimeUnit.MILLISECONDS.toSeconds(lastModMillis);
Path local1 = localDir.resolve("file-1.txt");
- byte[] data = Utils.writeFile(local1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
+ byte[] data = CommonTestSupportUtils.writeFile(local1, getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL);
File lclFile1 = local1.toFile();
boolean lcl1ModSet = lclFile1.setLastModified(lastModMillis);
@@ -646,7 +658,7 @@ public class ScpTest extends BaseTestSupport {
boolean lclSub2ModSet = lclSubFile2.setLastModified(lastModMillis);
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteDir);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteDir);
scp.upload(localDir.toString() + File.separator + "*", remotePath, ScpClient.Option.Recursive, ScpClient.Option.PreserveAttributes);
Path remote1 = remoteDir.resolve(local1.getFileName());
@@ -662,7 +674,7 @@ public class ScpTest extends BaseTestSupport {
File remSubFile2 = remoteSub2.toFile();
assertLastModifiedTimeEquals(remSubFile2, lclSub2ModSet, lastModSecs);
- Utils.deleteRecursive(localDir);
+ CommonTestSupportUtils.deleteRecursive(localDir);
assertHierarchyTargetFolderExists(localDir);
scp.download(remotePath + "/*", localDir, ScpClient.Option.Recursive, ScpClient.Option.PreserveAttributes);
@@ -684,12 +696,13 @@ public class ScpTest extends BaseTestSupport {
ScpClient scp = createScpClient(session);
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(scpRoot);
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(scpRoot);
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
Path remoteFile = remoteDir.resolve("file.txt");
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
outputDebugMessage("Upload data to %s", remotePath);
scp.upload(data, remotePath, EnumSet.allOf(PosixFilePermission.class), null);
@@ -753,8 +766,9 @@ public class ScpTest extends BaseTestSupport {
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(scpRoot);
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(scpRoot);
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot);
Path localFile = remoteDir.resolve("data.txt");
@@ -762,7 +776,7 @@ public class ScpTest extends BaseTestSupport {
Files.write(localFile, data);
Path remoteFile = remoteDir.resolve("upload.txt");
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
outputDebugMessage("Upload data to %s", remotePath);
scp.upload(localFile, remotePath);
assertFileLength(remoteFile, data.length, TimeUnit.SECONDS.toMillis(11L));
@@ -838,12 +852,13 @@ public class ScpTest extends BaseTestSupport {
ScpClient scp = creator.createScpClient(session);
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(scpRoot);
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(scpRoot);
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
Path remoteFile = remoteDir.resolve("file.txt");
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
outputDebugMessage("Upload data to %s", remotePath);
try {
@@ -918,7 +933,7 @@ public class ScpTest extends BaseTestSupport {
String unixPath = unixDir + "/" + fileName;
File root = new File(unixDir);
File target = new File(unixPath);
- Utils.deleteRecursive(root);
+ CommonTestSupportUtils.deleteRecursive(root);
root.mkdirs();
assertTrue("Failed to ensure existence of " + root, root.exists());
@@ -961,12 +976,13 @@ public class ScpTest extends BaseTestSupport {
public void testWithGanymede() throws Exception {
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(scpRoot);
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(scpRoot);
byte[] expected = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteDir);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteDir);
String fileName = "file.txt";
Path remoteFile = remoteDir.resolve(fileName);
String mode = ScpHelper.getOctalPermissions(EnumSet.of(
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-scp/src/test/java/org/apache/sshd/client/scp/SimpleScpClientTest.java
----------------------------------------------------------------------
diff --git a/sshd-scp/src/test/java/org/apache/sshd/client/scp/SimpleScpClientTest.java b/sshd-scp/src/test/java/org/apache/sshd/client/scp/SimpleScpClientTest.java
index d9b9b08..b66071a 100644
--- a/sshd-scp/src/test/java/org/apache/sshd/client/scp/SimpleScpClientTest.java
+++ b/sshd-scp/src/test/java/org/apache/sshd/client/scp/SimpleScpClientTest.java
@@ -29,7 +29,7 @@ import org.apache.sshd.common.scp.ScpHelper;
import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.util.io.IoUtils;
import org.apache.sshd.server.scp.ScpCommandFactory;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.apache.sshd.util.test.client.simple.BaseSimpleClientTestSupport;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -77,17 +77,18 @@ public class SimpleScpClientTest extends BaseSimpleClientTestSupport {
@Test
public void testScpUploadProxy() throws Exception {
try (CloseableScpClient scp = login()) {
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(scpRoot);
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(scpRoot);
Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
Path localFile = localDir.resolve("file.txt");
String data = getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL;
- byte[] written = Utils.writeFile(localFile, data);
+ byte[] written = CommonTestSupportUtils.writeFile(localFile, data);
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
Path remoteFile = remoteDir.resolve(localFile.getFileName());
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
scp.upload(localFile, remotePath);
byte[] uploaded = Files.readAllBytes(remoteFile);
@@ -98,16 +99,17 @@ public class SimpleScpClientTest extends BaseSimpleClientTestSupport {
@Test
public void testScpDownloadProxy() throws Exception {
try (CloseableScpClient scp = login()) {
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(scpRoot);
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath,
+ ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(scpRoot);
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
Path remoteFile = remoteDir.resolve("file.txt");
String data = getClass().getName() + "#" + getCurrentTestName() + IoUtils.EOL;
- byte[] written = Utils.writeFile(remoteFile, data);
+ byte[] written = CommonTestSupportUtils.writeFile(remoteFile, data);
Path localDir = assertHierarchyTargetFolderExists(scpRoot.resolve("local"));
Path localFile = localDir.resolve(remoteFile.getFileName());
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
scp.download(remotePath, localFile);
byte[] downloaded = Files.readAllBytes(localFile);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-sftp/pom.xml b/sshd-sftp/pom.xml
index e26eb32..f639511 100644
--- a/sshd-sftp/pom.xml
+++ b/sshd-sftp/pom.xml
@@ -42,7 +42,7 @@
<artifactId>sshd-core</artifactId>
<version>${project.version}</version>
</dependency>
-
+ <!-- Test dependencies -->
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
@@ -51,6 +51,13 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java
index df33c61..9810ba1 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java
@@ -74,7 +74,7 @@ import org.apache.sshd.client.subsystem.sftp.SftpClient.Attributes;
import org.apache.sshd.common.PropertyResolver;
import org.apache.sshd.common.PropertyResolverUtils;
import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
import org.apache.sshd.common.io.IoSession;
import org.apache.sshd.common.subsystem.sftp.SftpConstants;
import org.apache.sshd.common.subsystem.sftp.SftpException;
@@ -180,7 +180,7 @@ public class SftpFileSystemProvider extends FileSystemProvider {
String host = ValidateUtils.checkNotNullAndNotEmpty(uri.getHost(), "Host not provided");
int port = uri.getPort();
if (port <= 0) {
- port = SshConfigFileReader.DEFAULT_PORT;
+ port = ConfigFileReaderSupport.DEFAULT_PORT;
}
String userInfo = ValidateUtils.checkNotNullAndNotEmpty(uri.getUserInfo(), "UserInfo not provided");
@@ -1219,13 +1219,13 @@ public class SftpFileSystemProvider extends FileSystemProvider {
InetSocketAddress inetAddr = (InetSocketAddress) addr;
return getFileSystemIdentifier(inetAddr.getHostString(), inetAddr.getPort(), username);
} else {
- return getFileSystemIdentifier(addr.toString(), SshConfigFileReader.DEFAULT_PORT, username);
+ return getFileSystemIdentifier(addr.toString(), ConfigFileReaderSupport.DEFAULT_PORT, username);
}
}
public static String getFileSystemIdentifier(String host, int port, String username) {
return GenericUtils.trimToEmpty(host) + ':'
- + ((port <= 0) ? SshConfigFileReader.DEFAULT_PORT : port) + ':'
+ + ((port <= 0) ? ConfigFileReaderSupport.DEFAULT_PORT : port) + ':'
+ GenericUtils.trimToEmpty(username);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClientTestSupport.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClientTestSupport.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClientTestSupport.java
index 49e6c28..8d54edf 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClientTestSupport.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/AbstractSftpClientTestSupport.java
@@ -31,8 +31,8 @@ import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.apache.sshd.util.test.JSchLogger;
-import org.apache.sshd.util.test.Utils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@@ -55,12 +55,12 @@ public abstract class AbstractSftpClientTestSupport extends BaseTestSupport {
@BeforeClass
public static void setupClientAndServer() throws Exception {
JSchLogger.init();
- sshd = Utils.setupTestServer(AbstractSftpClientTestSupport.class);
+ sshd = CoreTestSupportUtils.setupTestServer(AbstractSftpClientTestSupport.class);
sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
sshd.start();
port = sshd.getPort();
- client = Utils.setupTestClient(AbstractSftpClientTestSupport.class);
+ client = CoreTestSupportUtils.setupTestClient(AbstractSftpClientTestSupport.class);
client.start();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java
index e5265d5..606e17e 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/DefaultCloseableHandleTest.java
@@ -26,7 +26,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.sshd.client.subsystem.sftp.SftpClient.CloseableHandle;
import org.apache.sshd.client.subsystem.sftp.SftpClient.Handle;
import org.apache.sshd.client.subsystem.sftp.impl.DefaultCloseableHandle;
-import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.JUnitTestSupport;
import org.apache.sshd.util.test.NoIoTestCase;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -40,7 +40,7 @@ import org.mockito.Mockito;
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Category({ NoIoTestCase.class })
-public class DefaultCloseableHandleTest extends BaseTestSupport {
+public class DefaultCloseableHandleTest extends JUnitTestSupport {
public DefaultCloseableHandleTest() {
super();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemTest.java
index 80428eb..9ecb690 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemTest.java
@@ -70,7 +70,8 @@ import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemEnvironment;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -94,7 +95,7 @@ public class SftpFileSystemTest extends BaseTestSupport {
@BeforeClass
public static void setupServerInstance() throws Exception {
- sshd = Utils.setupTestServer(SftpFileSystemTest.class);
+ sshd = CoreTestSupportUtils.setupTestServer(SftpFileSystemTest.class);
sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
sshd.start();
port = sshd.getPort();
@@ -157,8 +158,9 @@ public class SftpFileSystemTest extends BaseTestSupport {
@Test
public void testAttributes() throws IOException {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(lclSftp);
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath,
+ SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(lclSftp);
try (FileSystem fs = FileSystems.newFileSystem(createDefaultFileSystemURI(),
GenericUtils.<String, Object>mapBuilder()
@@ -168,7 +170,7 @@ public class SftpFileSystemTest extends BaseTestSupport {
Path parentPath = targetPath.getParent();
Path clientFolder = lclSftp.resolve("client");
- String remFilePath = Utils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file.txt"));
+ String remFilePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file.txt"));
Path file = fs.getPath(remFilePath);
assertHierarchyTargetFolderExists(file.getParent());
Files.write(file, (getCurrentTestName() + "\n").getBytes(StandardCharsets.UTF_8));
@@ -203,7 +205,7 @@ public class SftpFileSystemTest extends BaseTestSupport {
public void testRootFileSystem() throws IOException {
Path targetPath = detectTargetFolder();
Path rootNative = targetPath.resolve("root").toAbsolutePath();
- Utils.deleteRecursive(rootNative);
+ CommonTestSupportUtils.deleteRecursive(rootNative);
assertHierarchyTargetFolderExists(rootNative);
try (FileSystem fs = FileSystems.newFileSystem(URI.create("root:" + rootNative.toUri().toString() + "!/"), null)) {
@@ -215,13 +217,14 @@ public class SftpFileSystemTest extends BaseTestSupport {
@Test // see SSHD-697
public void testFileChannel() throws IOException {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath,
+ SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
Path lclFile = lclSftp.resolve(getCurrentTestName() + ".txt");
Files.deleteIfExists(lclFile);
byte[] expected = (getClass().getName() + "#" + getCurrentTestName() + "(" + new Date() + ")").getBytes(StandardCharsets.UTF_8);
try (FileSystem fs = FileSystems.newFileSystem(createDefaultFileSystemURI(), Collections.emptyMap())) {
Path parentPath = targetPath.getParent();
- String remFilePath = Utils.resolveRelativeRemotePath(parentPath, lclFile);
+ String remFilePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclFile);
Path file = fs.getPath(remFilePath);
FileSystemProvider provider = fs.provider();
@@ -376,15 +379,16 @@ public class SftpFileSystemTest extends BaseTestSupport {
}
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(lclSftp);
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath,
+ SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(lclSftp);
Path current = fs.getPath(".").toRealPath().normalize();
outputDebugMessage("CWD: %s", current);
Path parentPath = targetPath.getParent();
Path clientFolder = lclSftp.resolve("client");
- String remFile1Path = Utils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-1.txt"));
+ String remFile1Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-1.txt"));
Path file1 = fs.getPath(remFile1Path);
assertHierarchyTargetFolderExists(file1.getParent());
@@ -409,9 +413,9 @@ public class SftpFileSystemTest extends BaseTestSupport {
assertListEquals("Mismatched ACLs for " + file1, aclView.getAcl(), acl);
}
- String remFile2Path = Utils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-2.txt"));
+ String remFile2Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-2.txt"));
Path file2 = fs.getPath(remFile2Path);
- String remFile3Path = Utils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-3.txt"));
+ String remFile3Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-3.txt"));
Path file3 = fs.getPath(remFile3Path);
try {
outputDebugMessage("Move with failure expected %s => %s", file2, file3);
[21/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPrivateKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPrivateKeyEntryDecoder.java
deleted file mode 100644
index 574412e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPrivateKeyEntryDecoder.java
+++ /dev/null
@@ -1,40 +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.impl;
-
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Collection;
-
-import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
-
-/**
- * @param <PUB> Type of {@link PublicKey}
- * @param <PRV> Type of {@link PrivateKey}
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractPrivateKeyEntryDecoder<PUB extends PublicKey, PRV extends PrivateKey>
- extends AbstractKeyEntryResolver<PUB, PRV>
- implements PrivateKeyEntryDecoder<PUB, PRV> {
- protected AbstractPrivateKeyEntryDecoder(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
- super(pubType, prvType, names);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPublicKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPublicKeyEntryDecoder.java
deleted file mode 100644
index 59cbf3a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPublicKeyEntryDecoder.java
+++ /dev/null
@@ -1,41 +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.impl;
-
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Collection;
-
-import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
-
-/**
- * Useful base class implementation for a decoder of an {@code OpenSSH} encoded key data
- *
- * @param <PUB> Type of {@link PublicKey}
- * @param <PRV> Type of {@link PrivateKey}
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractPublicKeyEntryDecoder<PUB extends PublicKey, PRV extends PrivateKey>
- extends AbstractKeyEntryResolver<PUB, PRV>
- implements PublicKeyEntryDecoder<PUB, PRV> {
- protected AbstractPublicKeyEntryDecoder(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
- super(pubType, prvType, names);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/DSSPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/DSSPublicKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/DSSPublicKeyEntryDecoder.java
deleted file mode 100644
index 464d2b0..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/DSSPublicKeyEntryDecoder.java
+++ /dev/null
@@ -1,119 +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.impl;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.spec.DSAPrivateKeySpec;
-import java.security.spec.DSAPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Collections;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DSSPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<DSAPublicKey, DSAPrivateKey> {
- public static final DSSPublicKeyEntryDecoder INSTANCE = new DSSPublicKeyEntryDecoder();
-
- public DSSPublicKeyEntryDecoder() {
- super(DSAPublicKey.class, DSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_DSS)));
- }
-
- @Override
- public DSAPublicKey decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException {
- if (!KeyPairProvider.SSH_DSS.equals(keyType)) { // just in case we were invoked directly
- throw new InvalidKeySpecException("Unexpected key type: " + keyType);
- }
-
- BigInteger p = KeyEntryResolver.decodeBigInt(keyData);
- BigInteger q = KeyEntryResolver.decodeBigInt(keyData);
- BigInteger g = KeyEntryResolver.decodeBigInt(keyData);
- BigInteger y = KeyEntryResolver.decodeBigInt(keyData);
-
- return generatePublicKey(new DSAPublicKeySpec(y, p, q, g));
- }
-
- @Override
- public String encodePublicKey(OutputStream s, DSAPublicKey key) throws IOException {
- Objects.requireNonNull(key, "No public key provided");
-
- DSAParams keyParams = Objects.requireNonNull(key.getParams(), "No DSA params available");
- KeyEntryResolver.encodeString(s, KeyPairProvider.SSH_DSS);
- KeyEntryResolver.encodeBigInt(s, keyParams.getP());
- KeyEntryResolver.encodeBigInt(s, keyParams.getQ());
- KeyEntryResolver.encodeBigInt(s, keyParams.getG());
- KeyEntryResolver.encodeBigInt(s, key.getY());
-
- return KeyPairProvider.SSH_DSS;
- }
-
- @Override
- public DSAPublicKey clonePublicKey(DSAPublicKey key) throws GeneralSecurityException {
- if (key == null) {
- return null;
- }
-
- DSAParams params = key.getParams();
- if (params == null) {
- throw new InvalidKeyException("Missing parameters in key");
- }
-
- return generatePublicKey(new DSAPublicKeySpec(key.getY(), params.getP(), params.getQ(), params.getG()));
- }
-
- @Override
- public DSAPrivateKey clonePrivateKey(DSAPrivateKey key) throws GeneralSecurityException {
- if (key == null) {
- return null;
- }
-
- DSAParams params = key.getParams();
- if (params == null) {
- throw new InvalidKeyException("Missing parameters in key");
- }
-
- return generatePrivateKey(new DSAPrivateKeySpec(key.getX(), params.getP(), params.getQ(), params.getG()));
- }
-
- @Override
- public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
- return SecurityUtils.getKeyPairGenerator(KeyUtils.DSS_ALGORITHM);
- }
-
- @Override
- public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
- return SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
deleted file mode 100644
index 397a007..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
+++ /dev/null
@@ -1,178 +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.impl;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchProviderException;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPrivateKeySpec;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Objects;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ECDSAPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<ECPublicKey, ECPrivateKey> {
- public static final ECDSAPublicKeyEntryDecoder INSTANCE = new ECDSAPublicKeyEntryDecoder();
-
- // see rfc5480 section 2.2
- public static final byte ECPOINT_UNCOMPRESSED_FORM_INDICATOR = 0x04;
- public static final byte ECPOINT_COMPRESSED_VARIANT_2 = 0x02;
- public static final byte ECPOINT_COMPRESSED_VARIANT_3 = 0x02;
-
- public ECDSAPublicKeyEntryDecoder() {
- super(ECPublicKey.class, ECPrivateKey.class, ECCurves.KEY_TYPES);
- }
-
- @Override
- public ECPublicKey decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException {
- ECCurves curve = ECCurves.fromKeyType(keyType);
- if (curve == null) {
- throw new InvalidKeySpecException("Not an EC curve name: " + keyType);
- }
-
- if (!SecurityUtils.isECCSupported()) {
- throw new NoSuchProviderException("ECC not supported");
- }
-
- String keyCurveName = curve.getName();
- // see rfc5656 section 3.1
- String encCurveName = KeyEntryResolver.decodeString(keyData);
- if (!keyCurveName.equals(encCurveName)) {
- throw new InvalidKeySpecException("Mismatched key curve name (" + keyCurveName + ") vs. encoded one (" + encCurveName + ")");
- }
-
- byte[] octets = KeyEntryResolver.readRLEBytes(keyData);
- ECPoint w;
- try {
- w = ECCurves.octetStringToEcPoint(octets);
- if (w == null) {
- throw new InvalidKeySpecException("No ECPoint generated for curve=" + keyCurveName
- + " from octets=" + BufferUtils.toHex(':', octets));
- }
- } catch (RuntimeException e) {
- throw new InvalidKeySpecException("Failed (" + e.getClass().getSimpleName() + ")"
- + " to generate ECPoint for curve=" + keyCurveName
- + " from octets=" + BufferUtils.toHex(':', octets)
- + ": " + e.getMessage());
- }
-
- ECParameterSpec paramSpec = curve.getParameters();
- return generatePublicKey(new ECPublicKeySpec(w, paramSpec));
- }
-
- @Override
- public ECPublicKey clonePublicKey(ECPublicKey key) throws GeneralSecurityException {
- if (!SecurityUtils.isECCSupported()) {
- throw new NoSuchProviderException("ECC not supported");
- }
-
- if (key == null) {
- return null;
- }
-
- ECParameterSpec params = key.getParams();
- if (params == null) {
- throw new InvalidKeyException("Missing parameters in key");
- }
-
- return generatePublicKey(new ECPublicKeySpec(key.getW(), params));
- }
-
- @Override
- public ECPrivateKey clonePrivateKey(ECPrivateKey key) throws GeneralSecurityException {
- if (!SecurityUtils.isECCSupported()) {
- throw new NoSuchProviderException("ECC not supported");
- }
-
- if (key == null) {
- return null;
- }
-
- ECParameterSpec params = key.getParams();
- if (params == null) {
- throw new InvalidKeyException("Missing parameters in key");
- }
-
- return generatePrivateKey(new ECPrivateKeySpec(key.getS(), params));
- }
-
- @Override
- public String encodePublicKey(OutputStream s, ECPublicKey key) throws IOException {
- Objects.requireNonNull(key, "No public key provided");
-
- ECParameterSpec params = Objects.requireNonNull(key.getParams(), "No EC parameters available");
- ECCurves curve = Objects.requireNonNull(ECCurves.fromCurveParameters(params), "Cannot determine curve");
- String keyType = curve.getKeyType();
- String curveName = curve.getName();
- KeyEntryResolver.encodeString(s, keyType);
- // see rfc5656 section 3.1
- KeyEntryResolver.encodeString(s, curveName);
- ECCurves.ECPointCompression.UNCOMPRESSED.writeECPoint(s, curveName, key.getW());
- return keyType;
- }
-
- @Override
- public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
- if (SecurityUtils.isECCSupported()) {
- return SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
- } else {
- throw new NoSuchProviderException("ECC not supported");
- }
- }
-
- @Override
- public KeyPair generateKeyPair(int keySize) throws GeneralSecurityException {
- ECCurves curve = ECCurves.fromCurveSize(keySize);
- if (curve == null) {
- throw new InvalidKeySpecException("Unknown curve for key size=" + keySize);
- }
-
- KeyPairGenerator gen = getKeyPairGenerator();
- gen.initialize(curve.getParameters());
- return gen.generateKeyPair();
- }
-
- @Override
- public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
- if (SecurityUtils.isECCSupported()) {
- return SecurityUtils.getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
- } else {
- throw new NoSuchProviderException("ECC not supported");
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java
deleted file mode 100644
index 4550815..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java
+++ /dev/null
@@ -1,117 +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.impl;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.security.interfaces.RSAPrivateCrtKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.RSAPrivateCrtKeySpec;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.Collections;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class RSAPublicKeyDecoder extends AbstractPublicKeyEntryDecoder<RSAPublicKey, RSAPrivateKey> {
- public static final RSAPublicKeyDecoder INSTANCE = new RSAPublicKeyDecoder();
-
- public RSAPublicKeyDecoder() {
- super(RSAPublicKey.class, RSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_RSA)));
- }
-
- @Override
- public RSAPublicKey decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException {
- if (!KeyPairProvider.SSH_RSA.equals(keyType)) { // just in case we were invoked directly
- throw new InvalidKeySpecException("Unexpected key type: " + keyType);
- }
-
- BigInteger e = KeyEntryResolver.decodeBigInt(keyData);
- BigInteger n = KeyEntryResolver.decodeBigInt(keyData);
-
- return generatePublicKey(new RSAPublicKeySpec(n, e));
- }
-
- @Override
- public String encodePublicKey(OutputStream s, RSAPublicKey key) throws IOException {
- Objects.requireNonNull(key, "No public key provided");
- KeyEntryResolver.encodeString(s, KeyPairProvider.SSH_RSA);
- KeyEntryResolver.encodeBigInt(s, key.getPublicExponent());
- KeyEntryResolver.encodeBigInt(s, key.getModulus());
-
- return KeyPairProvider.SSH_RSA;
- }
-
- @Override
- public RSAPublicKey clonePublicKey(RSAPublicKey key) throws GeneralSecurityException {
- if (key == null) {
- return null;
- } else {
- return generatePublicKey(new RSAPublicKeySpec(key.getModulus(), key.getPublicExponent()));
- }
- }
-
- @Override
- public RSAPrivateKey clonePrivateKey(RSAPrivateKey key) throws GeneralSecurityException {
- if (key == null) {
- return null;
- }
-
- if (!(key instanceof RSAPrivateCrtKey)) {
- throw new InvalidKeyException("Cannot clone a non-RSAPrivateCrtKey: " + key.getClass().getSimpleName());
- }
-
- RSAPrivateCrtKey rsaPrv = (RSAPrivateCrtKey) key;
- return generatePrivateKey(
- new RSAPrivateCrtKeySpec(
- rsaPrv.getModulus(),
- rsaPrv.getPublicExponent(),
- rsaPrv.getPrivateExponent(),
- rsaPrv.getPrimeP(),
- rsaPrv.getPrimeQ(),
- rsaPrv.getPrimeExponentP(),
- rsaPrv.getPrimeExponentQ(),
- rsaPrv.getCrtCoefficient()));
- }
-
- @Override
- public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
- return SecurityUtils.getKeyPairGenerator(KeyUtils.RSA_ALGORITHM);
- }
-
- @Override
- public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
- return SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java
deleted file mode 100644
index 4437945..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java
+++ /dev/null
@@ -1,110 +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.loader;
-
-import java.security.GeneralSecurityException;
-import java.security.Key;
-import java.security.spec.InvalidKeySpecException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import javax.crypto.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 AESPrivateKeyObfuscator extends AbstractPrivateKeyObfuscator {
- public static final String CIPHER_NAME = "AES";
- public static final AESPrivateKeyObfuscator INSTANCE = new AESPrivateKeyObfuscator();
-
- public AESPrivateKeyObfuscator() {
- super(CIPHER_NAME);
- }
-
- @Override
- public List<Integer> getSupportedKeySizes() {
- return getAvailableKeyLengths();
- }
-
- @Override
- public byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException {
- int keyLength = resolveKeyLength(encContext);
- byte[] keyValue = deriveEncryptionKey(encContext, keyLength / Byte.SIZE);
- return applyPrivateKeyCipher(bytes, encContext, keyLength, keyValue, encryptIt);
- }
-
- @Override
- protected int resolveKeyLength(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException {
- String cipherType = encContext.getCipherType();
- try {
- int keyLength = Integer.parseInt(cipherType);
- List<Integer> sizes = getSupportedKeySizes();
- for (Integer s : sizes) {
- if (s.intValue() == keyLength) {
- return keyLength;
- }
- }
-
- throw new InvalidKeySpecException("Unknown " + getCipherName() + " key length: " + cipherType + " - supported: " + sizes);
- } catch (NumberFormatException e) {
- throw new InvalidKeySpecException("Bad " + getCipherName() + " key length (" + cipherType + "): " + e.getMessage(), e);
- }
- }
-
- /**
- * @return A {@link List} of {@link Integer}s holding the available key
- * lengths values (in bits) for the JVM. <B>Note:</B> AES 256 requires
- * special JCE policy extension installation (e.g., for Java 7 see
- * <A HREF="http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html">this link</A>)
- */
- @SuppressWarnings("synthetic-access")
- public static List<Integer> getAvailableKeyLengths() {
- return LazyKeyLengthsHolder.KEY_LENGTHS;
- }
-
- private static final class LazyKeyLengthsHolder {
- private static final List<Integer> KEY_LENGTHS = Collections.unmodifiableList(detectSupportedKeySizes());
-
- private LazyKeyLengthsHolder() {
- throw new UnsupportedOperationException("No instance allowed");
- }
-
- // AES 256 requires special JCE policy extension installation
- private static List<Integer> detectSupportedKeySizes() {
- List<Integer> sizes = new ArrayList<>();
- for (int keyLength = 128; keyLength < Short.MAX_VALUE /* just so it doesn't go forever */; keyLength += 64) {
- try {
- byte[] keyAsBytes = new byte[keyLength / Byte.SIZE];
- Key key = new SecretKeySpec(keyAsBytes, CIPHER_NAME);
- Cipher c = SecurityUtils.getCipher(CIPHER_NAME);
- c.init(Cipher.DECRYPT_MODE, key);
- sizes.add(Integer.valueOf(keyLength));
- } catch (GeneralSecurityException e) {
- return sizes;
- }
- }
-
- throw new IllegalStateException("No limit encountered: " + sizes);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
deleted file mode 100644
index a83bf68..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
+++ /dev/null
@@ -1,181 +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.loader;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractKeyPairResourceParser extends AbstractLoggingBean implements KeyPairResourceParser {
- private final List<String> beginners;
- private final List<String> enders;
- private final List<List<String>> endingMarkers;
-
- /**
- * @param beginners The markers that indicate the beginning of a parsing block
- * @param enders The <U>matching</U> (by position) markers that indicate the end of a parsing block
- */
- protected AbstractKeyPairResourceParser(List<String> beginners, List<String> enders) {
- this.beginners = ValidateUtils.checkNotNullAndNotEmpty(beginners, "No begin markers");
- this.enders = ValidateUtils.checkNotNullAndNotEmpty(enders, "No end markers");
- ValidateUtils.checkTrue(
- beginners.size() == enders.size(), "Mismatched begin(%d)/end(%d) markers sizes", beginners.size(), enders.size());
- endingMarkers = new ArrayList<>(enders.size());
- enders.forEach(m -> endingMarkers.add(Collections.singletonList(m)));
- }
-
- public List<String> getBeginners() {
- return beginners;
- }
-
- public List<String> getEnders() {
- return enders;
- }
-
- /**
- * @return A {@link List} of same size as the ending markers, where
- * each ending marker is encapsulated inside a singleton list and
- * resides as the <U>same index</U> as the marker it encapsulates
- */
- public List<List<String>> getEndingMarkers() {
- return endingMarkers;
- }
-
- @Override
- public boolean canExtractKeyPairs(String resourceKey, List<String> lines) throws IOException, GeneralSecurityException {
- return KeyPairResourceParser.containsMarkerLine(lines, getBeginners());
- }
-
- @Override
- public Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
- throws IOException, GeneralSecurityException {
- Collection<KeyPair> keyPairs = Collections.emptyList();
- List<String> beginMarkers = getBeginners();
- List<List<String>> endMarkers = getEndingMarkers();
- for (Map.Entry<Integer, Integer> markerPos = KeyPairResourceParser.findMarkerLine(lines, beginMarkers); markerPos != null;) {
- int startIndex = markerPos.getKey();
- String startLine = lines.get(startIndex);
- startIndex++;
-
- int markerIndex = markerPos.getValue();
- List<String> ender = endMarkers.get(markerIndex);
- markerPos = KeyPairResourceParser.findMarkerLine(lines, startIndex, ender);
- if (markerPos == null) {
- throw new StreamCorruptedException("Missing end marker (" + ender + ") after line #" + startIndex);
- }
-
- int endIndex = markerPos.getKey();
- String endLine = lines.get(endIndex);
- Collection<KeyPair> kps =
- extractKeyPairs(resourceKey, startLine, endLine, passwordProvider, lines.subList(startIndex, endIndex));
- if (GenericUtils.isNotEmpty(kps)) {
- if (GenericUtils.isEmpty(keyPairs)) {
- keyPairs = new LinkedList<>(kps);
- } else {
- keyPairs.addAll(kps);
- }
- }
-
- // see if there are more
- markerPos = KeyPairResourceParser.findMarkerLine(lines, endIndex + 1, beginMarkers);
- }
-
- return keyPairs;
- }
-
- /**
- * Extracts the key pairs within a <U>single</U> delimited by markers block of lines. By
- * default cleans up the empty lines, joins them and converts them from BASE64
- *
- * @param resourceKey A hint as to the origin of the text lines
- * @param beginMarker The line containing the begin marker
- * @param endMarker The line containing the end marker
- * @param passwordProvider The {@link FilePasswordProvider} to use
- * in case the data is encrypted - may be {@code null} if no encrypted
- * @param lines The block of lines between the markers
- * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
- * @throws IOException If failed to parse the data
- * @throws GeneralSecurityException If failed to generate the keys
- * @see #extractKeyPairs(String, String, String, FilePasswordProvider, byte[])
- */
- public Collection<KeyPair> extractKeyPairs(
- String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
- throws IOException, GeneralSecurityException {
- return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, KeyPairResourceParser.extractDataBytes(lines));
- }
-
- /**
- * @param resourceKey A hint as to the origin of the text lines
- * @param beginMarker The line containing the begin marker
- * @param endMarker The line containing the end marker
- * @param passwordProvider The {@link FilePasswordProvider} to use
- * in case the data is encrypted - may be {@code null} if no encrypted
- * @param bytes The decoded bytes from the lines containing the data
- * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
- * @throws IOException If failed to parse the data
- * @throws GeneralSecurityException If failed to generate the keys
- * @see #extractKeyPairs(String, String, String, FilePasswordProvider, InputStream)
- */
- public Collection<KeyPair> extractKeyPairs(
- String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, byte[] bytes)
- throws IOException, GeneralSecurityException {
- if (log.isTraceEnabled()) {
- BufferUtils.dumpHex(getSimplifiedLogger(), Level.FINER, beginMarker, ':', 16, bytes);
- }
-
- try (InputStream bais = new ByteArrayInputStream(bytes)) {
- return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, bais);
- }
- }
-
- /**
- * @param resourceKey A hint as to the origin of the text lines
- * @param beginMarker The line containing the begin marker
- * @param endMarker The line containing the end marker
- * @param passwordProvider The {@link FilePasswordProvider} to use
- * in case the data is encrypted - may be {@code null} if no encrypted
- * @param stream The decoded data {@link InputStream}
- * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
- * @throws IOException If failed to parse the data
- * @throws GeneralSecurityException If failed to generate the keys
- */
- public abstract Collection<KeyPair> extractKeyPairs(
- String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java
deleted file mode 100644
index ac93ab4..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java
+++ /dev/null
@@ -1,191 +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.loader;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
-import java.security.SecureRandom;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import java.util.Objects;
-import java.util.Random;
-
-import javax.crypto.Cipher;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.sshd.common.digest.BuiltinDigests;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractPrivateKeyObfuscator implements PrivateKeyObfuscator {
- private final String algName;
-
- protected AbstractPrivateKeyObfuscator(String name) {
- algName = ValidateUtils.checkNotNullAndNotEmpty(name, "No name specified");
- }
-
- @Override
- public final String getCipherName() {
- return algName;
- }
-
- @Override
- public byte[] generateInitializationVector(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException {
- return generateInitializationVector(resolveKeyLength(encContext));
- }
-
- @Override
- public <A extends Appendable> A appendPrivateKeyEncryptionContext(A sb, PrivateKeyEncryptionContext encContext) throws IOException {
- if (encContext == null) {
- return sb;
- }
-
- sb.append("DEK-Info: ").append(encContext.getCipherName())
- .append('-').append(encContext.getCipherType())
- .append('-').append(encContext.getCipherMode());
-
- byte[] initVector = encContext.getInitVector();
- Objects.requireNonNull(initVector, "No encryption init vector");
- ValidateUtils.checkTrue(initVector.length > 0, "Empty encryption init vector");
- BufferUtils.appendHex(sb.append(','), BufferUtils.EMPTY_HEX_SEPARATOR, initVector);
- sb.append(System.lineSeparator());
- return sb;
- }
-
- protected byte[] generateInitializationVector(int keyLength) {
- int keySize = keyLength / Byte.SIZE;
- if ((keyLength % Byte.SIZE) != 0) { // e.g., if 36-bits then we need 5 bytes to hold
- keySize++;
- }
-
- byte[] initVector = new byte[keySize];
- Random randomizer = new SecureRandom(); // TODO consider using some pre-created singleton instance
- randomizer.nextBytes(initVector);
- return initVector;
- }
-
- protected abstract int resolveKeyLength(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException;
-
- // see http://martin.kleppmann.com/2013/05/24/improving-security-of-ssh-private-keys.html
- // see http://www.ict.griffith.edu.au/anthony/info/crypto/openssl.hints (Password to Encryption Key section)
- // see http://openssl.6102.n7.nabble.com/DES-EDE3-CBC-technical-details-td24883.html
- protected byte[] deriveEncryptionKey(PrivateKeyEncryptionContext encContext, int outputKeyLength) throws GeneralSecurityException {
- Objects.requireNonNull(encContext, "No encryption context");
- ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherName(), "No cipher name");
- ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherType(), "No cipher type");
- ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherMode(), "No cipher mode");
-
- byte[] initVector = Objects.requireNonNull(encContext.getInitVector(), "No encryption init vector");
- ValidateUtils.checkTrue(initVector.length > 0, "Empty encryption init vector");
-
- String password = ValidateUtils.checkNotNullAndNotEmpty(encContext.getPassword(), "No encryption password");
- byte[] passBytes = password.getBytes(StandardCharsets.UTF_8);
- byte[] keyValue = new byte[outputKeyLength];
- MessageDigest hash = SecurityUtils.getMessageDigest(BuiltinDigests.Constants.MD5);
- byte[] prevHash = GenericUtils.EMPTY_BYTE_ARRAY;
- for (int index = 0, remLen = keyValue.length; index < keyValue.length;) {
- hash.reset(); // just making sure
-
- hash.update(prevHash, 0, prevHash.length);
- hash.update(passBytes, 0, passBytes.length);
- hash.update(initVector, 0, Math.min(initVector.length, 8));
-
- prevHash = hash.digest();
-
- System.arraycopy(prevHash, 0, keyValue, index, Math.min(remLen, prevHash.length));
- index += prevHash.length;
- remLen -= prevHash.length;
- }
-
- return keyValue;
- }
-
- protected byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, int numBits, byte[] keyValue, boolean encryptIt)
- throws GeneralSecurityException {
- Objects.requireNonNull(encContext, "No encryption context");
- String cipherName = ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherName(), "No cipher name");
- ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherType(), "No cipher type");
- String cipherMode = ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherMode(), "No cipher mode");
-
- Objects.requireNonNull(bytes, "No source data");
- Objects.requireNonNull(keyValue, "No encryption key");
- ValidateUtils.checkTrue(keyValue.length > 0, "Empty encryption key");
-
- byte[] initVector = Objects.requireNonNull(encContext.getInitVector(), "No encryption init vector");
- ValidateUtils.checkTrue(initVector.length > 0, "Empty encryption init vector");
-
- String xform = cipherName + "/" + cipherMode + "/NoPadding";
- int maxAllowedBits = Cipher.getMaxAllowedKeyLength(xform);
- // see http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
- if (numBits > maxAllowedBits) {
- throw new InvalidKeySpecException("applyPrivateKeyCipher(" + xform + ")[encrypt=" + encryptIt + "]"
- + " required key length (" + numBits + ")"
- + " exceeds max. available: " + maxAllowedBits);
- }
-
- SecretKeySpec skeySpec = new SecretKeySpec(keyValue, cipherName);
- IvParameterSpec ivspec = new IvParameterSpec(initVector);
- Cipher cipher = SecurityUtils.getCipher(xform);
- int blockSize = cipher.getBlockSize();
- int dataSize = bytes.length;
- cipher.init(encryptIt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, skeySpec, ivspec);
- if (blockSize <= 0) {
- return cipher.doFinal(bytes);
- }
-
- int remLen = dataSize % blockSize;
- if (remLen <= 0) {
- return cipher.doFinal(bytes);
- }
-
- int updateSize = dataSize - remLen;
- byte[] lastBlock = new byte[blockSize];
- Arrays.fill(lastBlock, (byte) 10);
- System.arraycopy(bytes, updateSize, lastBlock, 0, remLen);
-
- // TODO for some reason, calling cipher.update followed by cipher.doFinal does not work
- ByteArrayOutputStream baos = new ByteArrayOutputStream(dataSize);
- try {
- try {
- byte[] buf = cipher.update(bytes, 0, updateSize);
- baos.write(buf);
-
- buf = cipher.doFinal(lastBlock);
- baos.write(buf);
- } finally {
- baos.close();
- }
- } catch (IOException e) {
- throw new GeneralSecurityException("applyPrivateKeyCipher(" + xform + ")[encrypt=" + encryptIt + "]"
- + " failed (" + e.getClass().getSimpleName() + ")"
- + " to split-write: " + e.getMessage(), e);
- }
-
- return baos.toByteArray();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java
deleted file mode 100644
index 2043f06..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java
+++ /dev/null
@@ -1,76 +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.loader;
-
-import java.security.GeneralSecurityException;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DESPrivateKeyObfuscator extends AbstractPrivateKeyObfuscator {
- public static final int DEFAULT_KEY_LENGTH = 24 /* hardwired size for 3DES */;
- public static final List<Integer> AVAILABLE_KEY_LENGTHS =
- Collections.unmodifiableList(Collections.singletonList(Integer.valueOf(DEFAULT_KEY_LENGTH)));
- public static final DESPrivateKeyObfuscator INSTANCE = new DESPrivateKeyObfuscator();
-
- public DESPrivateKeyObfuscator() {
- super("DES");
- }
-
- @Override
- public byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException {
- PrivateKeyEncryptionContext effContext = resolveEffectiveContext(encContext);
- byte[] keyValue = deriveEncryptionKey(effContext, DEFAULT_KEY_LENGTH);
- return applyPrivateKeyCipher(bytes, effContext, keyValue.length * Byte.SIZE, keyValue, encryptIt);
- }
-
- @Override
- public List<Integer> getSupportedKeySizes() {
- return AVAILABLE_KEY_LENGTHS;
- }
-
- @Override
- protected int resolveKeyLength(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException {
- return DEFAULT_KEY_LENGTH;
- }
-
- @Override
- protected byte[] generateInitializationVector(int keyLength) {
- return super.generateInitializationVector(8 * Byte.SIZE);
- }
-
- public static final PrivateKeyEncryptionContext resolveEffectiveContext(PrivateKeyEncryptionContext encContext) {
- if (encContext == null) {
- return null;
- }
-
- String cipherName = encContext.getCipherName();
- String cipherType = encContext.getCipherType();
- PrivateKeyEncryptionContext effContext = encContext;
- if ("EDE3".equalsIgnoreCase(cipherType)) {
- cipherName += "ede";
- effContext = encContext.clone();
- effContext.setCipherName(cipherName);
- }
-
- return effContext;
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
deleted file mode 100644
index fa6930a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
+++ /dev/null
@@ -1,129 +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.loader;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StringReader;
-import java.net.URL;
-import java.nio.charset.Charset;
-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.KeyPair;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.util.io.IoUtils;
-
-/**
- * Loads {@link KeyPair}s from text resources
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface KeyPairResourceLoader {
- /**
- * An empty loader that never fails but always returns an empty list
- */
- KeyPairResourceLoader EMPTY = (resourceKey, passwordProvider, lines) -> Collections.emptyList();
-
- default Collection<KeyPair> loadKeyPairs(Path path, FilePasswordProvider passwordProvider, OpenOption... options)
- throws IOException, GeneralSecurityException {
- return loadKeyPairs(path, passwordProvider, StandardCharsets.UTF_8, options);
- }
-
- default Collection<KeyPair> loadKeyPairs(Path path, FilePasswordProvider passwordProvider, Charset cs, OpenOption... options)
- throws IOException, GeneralSecurityException {
- try (InputStream stream = Files.newInputStream(path, options)) {
- return loadKeyPairs(path.toString(), passwordProvider, stream, cs);
- }
- }
-
- default Collection<KeyPair> loadKeyPairs(URL url, FilePasswordProvider passwordProvider)
- throws IOException, GeneralSecurityException {
- return loadKeyPairs(url, passwordProvider, StandardCharsets.UTF_8);
- }
-
- default Collection<KeyPair> loadKeyPairs(URL url, FilePasswordProvider passwordProvider, Charset cs)
- throws IOException, GeneralSecurityException {
- try (InputStream stream = Objects.requireNonNull(url, "No URL").openStream()) {
- return loadKeyPairs(url.toExternalForm(), passwordProvider, stream, cs);
- }
- }
-
- default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, String data)
- throws IOException, GeneralSecurityException {
- try (Reader reader = new StringReader((data == null) ? "" : data)) {
- return loadKeyPairs(resourceKey, passwordProvider, reader);
- }
- }
-
- default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
- return loadKeyPairs(resourceKey, passwordProvider, stream, StandardCharsets.UTF_8);
- }
-
- default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, InputStream stream, Charset cs)
- throws IOException, GeneralSecurityException {
- try (Reader reader = new InputStreamReader(
- Objects.requireNonNull(stream, "No stream instance"), Objects.requireNonNull(cs, "No charset"))) {
- return loadKeyPairs(resourceKey, passwordProvider, reader);
- }
- }
-
- default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, Reader r)
- throws IOException, GeneralSecurityException {
- try (BufferedReader br = new BufferedReader(Objects.requireNonNull(r, "No reader instance"), IoUtils.DEFAULT_COPY_SIZE)) {
- return loadKeyPairs(resourceKey, passwordProvider, br);
- }
- }
-
- default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, BufferedReader r)
- throws IOException, GeneralSecurityException {
- return loadKeyPairs(resourceKey, passwordProvider, IoUtils.readAllLines(r));
- }
-
- /**
- * Loads key pairs from the given resource text lines
- *
- * @param resourceKey A hint as to the origin of the text lines
- * @param passwordProvider The {@link FilePasswordProvider} to use
- * in case the data is encrypted - may be {@code null} if no encrypted
- * data is expected
- * @param lines The {@link List} of lines as read from the resource
- * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
- * <B>Note:</B> the resource loader may decide to skip unknown lines if
- * more than one key pair type is encoded in it
- * @throws IOException If failed to process the lines
- * @throws GeneralSecurityException If failed to generate the keys from the
- * parsed data
- */
- Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
- throws IOException, GeneralSecurityException;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
deleted file mode 100644
index 80fc2c5..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
+++ /dev/null
@@ -1,192 +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.loader;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Arrays;
-import java.util.Base64;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-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 interface KeyPairResourceParser extends KeyPairResourceLoader {
- /**
- * An empty parser that never fails, but always report that it cannot
- * extract key pairs and returns empty list if asked to load
- */
- KeyPairResourceParser EMPTY = new KeyPairResourceParser() {
- @Override
- public Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
- throws IOException, GeneralSecurityException {
- return Collections.emptyList();
- }
-
- @Override
- public boolean canExtractKeyPairs(String resourceKey, List<String> lines) throws IOException, GeneralSecurityException {
- return false;
- }
-
- @Override
- public String toString() {
- return "EMPTY";
- }
- };
-
- /**
- * @param resourceKey A hint as to the origin of the text lines
- * @param lines The resource lines
- * @return {@code true} if the parser can extract some key pairs from the lines
- * @throws IOException If failed to process the lines
- * @throws GeneralSecurityException If failed to extract information regarding
- * the possibility to extract the key pairs
- */
- boolean canExtractKeyPairs(String resourceKey, List<String> lines)
- throws IOException, GeneralSecurityException;
-
- /**
- * Converts the lines assumed to contain BASE-64 encoded data into
- * the actual content bytes.
- *
- * @param lines The data lines - empty lines and spaces are automatically
- * deleted <U>before</U> BASE-64 decoding takes place.
- * @return The decoded data bytes
- * @see #joinDataLines(Collection)
- */
- static byte[] extractDataBytes(Collection<String> lines) {
- String data = joinDataLines(lines);
- Base64.Decoder decoder = Base64.getDecoder();
- return decoder.decode(data);
- }
-
- static String joinDataLines(Collection<String> lines) {
- String data = GenericUtils.join(lines, ' ');
- data = data.replaceAll("\\s", "");
- data = data.trim();
- return data;
- }
-
- static boolean containsMarkerLine(List<String> lines, String marker) {
- return containsMarkerLine(lines, Collections.singletonList(ValidateUtils.checkNotNullAndNotEmpty(marker, "No marker")));
- }
-
- static boolean containsMarkerLine(List<String> lines, List<String> markers) {
- return findMarkerLine(lines, markers) != null;
- }
-
- /**
- * Attempts to locate a line that contains one of the markers
- *
- * @param lines The list of lines to scan - ignored if {@code null}/empty
- * @param markers The markers to match - ignored if {@code null}/empty
- * @return A {@link SimpleImmutableEntry} whose key is the <U>first</U> line index
- * that matched and value the matched marker index - {@code null} if no match found
- * @see #findMarkerLine(List, int, List)
- */
- static SimpleImmutableEntry<Integer, Integer> findMarkerLine(List<String> lines, List<String> markers) {
- return findMarkerLine(lines, 0, markers);
- }
-
- /**
- * Attempts to locate a line that contains one of the markers
- *
- * @param lines The list of lines to scan - ignored if {@code null}/empty
- * @param startLine The scan start line index
- * @param markers The markers to match - ignored if {@code null}/empty
- * @return A {@link SimpleImmutableEntry} whose key is the <U>first</U> line index
- * that matched and value the matched marker index - {@code null} if no match found
- */
- static SimpleImmutableEntry<Integer, Integer> findMarkerLine(List<String> lines, int startLine, List<String> markers) {
- if (GenericUtils.isEmpty(lines) || GenericUtils.isEmpty(markers)) {
- return null;
- }
-
- for (int lineIndex = startLine; lineIndex < lines.size(); lineIndex++) {
- String l = lines.get(lineIndex);
- for (int markerIndex = 0; markerIndex < markers.size(); markerIndex++) {
- String m = markers.get(markerIndex);
- if (l.contains(m)) {
- return new SimpleImmutableEntry<>(lineIndex, markerIndex);
- }
- }
- }
-
- return null;
- }
-
- static KeyPairResourceParser aggregate(KeyPairResourceParser... parsers) {
- return aggregate(Arrays.asList(ValidateUtils.checkNotNullAndNotEmpty(parsers, "No parsers to aggregate")));
- }
-
- static KeyPairResourceParser aggregate(Collection<? extends KeyPairResourceParser> parsers) {
- ValidateUtils.checkNotNullAndNotEmpty(parsers, "No parsers to aggregate");
- return new KeyPairResourceParser() {
- @Override
- public Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
- throws IOException, GeneralSecurityException {
- Collection<KeyPair> keyPairs = Collections.emptyList();
- for (KeyPairResourceParser p : parsers) {
- if (!p.canExtractKeyPairs(resourceKey, lines)) {
- continue;
- }
-
- Collection<KeyPair> kps = p.loadKeyPairs(resourceKey, passwordProvider, lines);
- if (GenericUtils.isEmpty(kps)) {
- continue;
- }
-
- if (GenericUtils.isEmpty(keyPairs)) {
- keyPairs = new LinkedList<>(kps);
- } else {
- keyPairs.addAll(kps);
- }
- }
-
- return keyPairs;
- }
-
- @Override
- public boolean canExtractKeyPairs(String resourceKey, List<String> lines) throws IOException, GeneralSecurityException {
- for (KeyPairResourceParser p : parsers) {
- if (p.canExtractKeyPairs(resourceKey, lines)) {
- return true;
- }
- }
-
- return false;
- }
-
- @Override
- public String toString() {
- return KeyPairResourceParser.class.getSimpleName() + "[aggregate]";
- }
- };
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyEncryptionContext.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyEncryptionContext.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyEncryptionContext.java
deleted file mode 100644
index 5e03cdb..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyEncryptionContext.java
+++ /dev/null
@@ -1,270 +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.loader;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableSet;
-import java.util.Objects;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-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 class PrivateKeyEncryptionContext implements Cloneable {
- public static final String DEFAULT_CIPHER_MODE = "CBC";
-
- private static final Map<String, PrivateKeyObfuscator> OBFUSCATORS =
- Stream.of(AESPrivateKeyObfuscator.INSTANCE, DESPrivateKeyObfuscator.INSTANCE)
- .collect(Collectors.toMap(AbstractPrivateKeyObfuscator::getCipherName, Function.identity()));
-
- private String cipherName;
- private String cipherType;
- private String cipherMode = DEFAULT_CIPHER_MODE;
- private String password;
- private byte[] initVector;
- private transient PrivateKeyObfuscator obfuscator;
-
- public PrivateKeyEncryptionContext() {
- super();
- }
-
- public PrivateKeyEncryptionContext(String algInfo) {
- parseAlgorithmInfo(algInfo);
- }
-
- public String getCipherName() {
- return cipherName;
- }
-
- public void setCipherName(String value) {
- cipherName = value;
- }
-
- public String getCipherType() {
- return cipherType;
- }
-
- public void setCipherType(String value) {
- cipherType = value;
- }
-
- public String getCipherMode() {
- return cipherMode;
- }
-
- public void setCipherMode(String value) {
- cipherMode = value;
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setPassword(String value) {
- password = value;
- }
-
- public byte[] getInitVector() {
- return initVector;
- }
-
- public void setInitVector(byte... values) {
- initVector = values;
- }
-
- public PrivateKeyObfuscator getPrivateKeyObfuscator() {
- return obfuscator;
- }
-
- public void setPrivateKeyObfuscator(PrivateKeyObfuscator value) {
- obfuscator = value;
- }
-
- public PrivateKeyObfuscator resolvePrivateKeyObfuscator() {
- PrivateKeyObfuscator value = getPrivateKeyObfuscator();
- if (value != null) {
- return value;
- }
-
- return getRegisteredPrivateKeyObfuscator(getCipherName());
- }
-
- public static PrivateKeyObfuscator registerPrivateKeyObfuscator(PrivateKeyObfuscator o) {
- return registerPrivateKeyObfuscator(Objects.requireNonNull(o, "No instance provided").getCipherName(), o);
- }
-
- public static PrivateKeyObfuscator registerPrivateKeyObfuscator(String cipherName, PrivateKeyObfuscator o) {
- ValidateUtils.checkNotNullAndNotEmpty(cipherName, "No cipher name");
- Objects.requireNonNull(o, "No instance provided");
-
- synchronized (OBFUSCATORS) {
- return OBFUSCATORS.put(cipherName, o);
- }
- }
-
- public static boolean unregisterPrivateKeyObfuscator(PrivateKeyObfuscator o) {
- Objects.requireNonNull(o, "No instance provided");
- String cipherName = o.getCipherName();
- ValidateUtils.checkNotNullAndNotEmpty(cipherName, "No cipher name");
-
- synchronized (OBFUSCATORS) {
- PrivateKeyObfuscator prev = OBFUSCATORS.get(cipherName);
- if (prev != o) {
- return false;
- }
-
- OBFUSCATORS.remove(cipherName);
- }
-
- return true;
- }
-
- public static PrivateKeyObfuscator unregisterPrivateKeyObfuscator(String cipherName) {
- ValidateUtils.checkNotNullAndNotEmpty(cipherName, "No cipher name");
-
- synchronized (OBFUSCATORS) {
- return OBFUSCATORS.remove(cipherName);
- }
- }
-
- public static final PrivateKeyObfuscator getRegisteredPrivateKeyObfuscator(String cipherName) {
- if (GenericUtils.isEmpty(cipherName)) {
- return null;
- }
-
- synchronized (OBFUSCATORS) {
- return OBFUSCATORS.get(cipherName);
- }
- }
-
- public static final NavigableSet<String> getRegisteredPrivateKeyObfuscatorCiphers() {
- synchronized (OBFUSCATORS) {
- Collection<String> names = OBFUSCATORS.keySet();
- return GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER, names);
- }
- }
-
- public static final List<PrivateKeyObfuscator> getRegisteredPrivateKeyObfuscators() {
- synchronized (OBFUSCATORS) {
- Collection<? extends PrivateKeyObfuscator> l = OBFUSCATORS.values();
- if (GenericUtils.isEmpty(l)) {
- return Collections.emptyList();
- } else {
- return new ArrayList<>(l);
- }
- }
- }
-
- /**
- * @param algInfo The algorithm info - format: <I>{@code name-type-mode}</I>
- * @return The updated context instance
- * @see #parseAlgorithmInfo(PrivateKeyEncryptionContext, String)
- */
- public PrivateKeyEncryptionContext parseAlgorithmInfo(String algInfo) {
- return parseAlgorithmInfo(this, algInfo);
- }
-
- @Override
- public PrivateKeyEncryptionContext clone() {
- try {
- PrivateKeyEncryptionContext copy = getClass().cast(super.clone());
- byte[] v = copy.getInitVector();
- if (v != null) {
- v = v.clone();
- copy.setInitVector(v);
- }
- return copy;
- } catch (CloneNotSupportedException e) { // unexpected
- throw new RuntimeException("Failed to clone: " + toString());
- }
- }
-
- @Override
- public int hashCode() {
- return GenericUtils.hashCode(getCipherName(), Boolean.TRUE)
- + GenericUtils.hashCode(getCipherType(), Boolean.TRUE)
- + GenericUtils.hashCode(getCipherMode(), Boolean.TRUE)
- + Objects.hashCode(getPassword())
- + Arrays.hashCode(getInitVector());
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (this == obj) {
- return true;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
-
- PrivateKeyEncryptionContext other = (PrivateKeyEncryptionContext) obj;
- return (GenericUtils.safeCompare(getCipherName(), other.getCipherName(), false) == 0)
- && (GenericUtils.safeCompare(getCipherType(), other.getCipherType(), false) == 0)
- && (GenericUtils.safeCompare(getCipherMode(), other.getCipherMode(), false) == 0)
- && (GenericUtils.safeCompare(getPassword(), other.getPassword(), true) == 0)
- && Arrays.equals(getInitVector(), other.getInitVector());
- }
-
- @Override
- public String toString() {
- return GenericUtils.join(new String[]{getCipherName(), getCipherType(), getCipherMode()}, '-');
- }
-
- /**
- * @param <C> Generic context type
- * @param context The {@link PrivateKeyEncryptionContext} to update
- * @param algInfo The algorithm info - format: {@code <I>name</I>-<I>type</I>-<I>mode</I>}
- * @return The updated context
- */
- public static final <C extends PrivateKeyEncryptionContext> C parseAlgorithmInfo(C context, String algInfo) {
- ValidateUtils.checkNotNullAndNotEmpty(algInfo, "No encryption algorithm data");
-
- String[] cipherData = GenericUtils.split(algInfo, '-');
- ValidateUtils.checkTrue(cipherData.length == 3, "Bad encryption algorithm data: %s", algInfo);
-
- context.setCipherName(cipherData[0]);
- context.setCipherType(cipherData[1]);
- context.setCipherMode(cipherData[2]);
- return context;
- }
-
- public static final PrivateKeyEncryptionContext newPrivateKeyEncryptionContext(PrivateKeyObfuscator o, String password) {
- return initializeObfuscator(new PrivateKeyEncryptionContext(), o, password);
- }
-
- public static final <C extends PrivateKeyEncryptionContext> C initializeObfuscator(C context, PrivateKeyObfuscator o, String password) {
- context.setCipherName(o.getCipherName());
- context.setPrivateKeyObfuscator(o);
- context.setPassword(password);
- return context;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyObfuscator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyObfuscator.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyObfuscator.java
deleted file mode 100644
index d8d2db5..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyObfuscator.java
+++ /dev/null
@@ -1,64 +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.loader;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.util.List;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface PrivateKeyObfuscator {
- /**
- * @return Basic cipher used to obfuscate
- */
- String getCipherName();
-
- /**
- * @return A {@link List} of the supported key sizes - <B>Note:</B> every
- * call returns a and <U>un-modifiable</U> instance.
- */
- List<Integer> getSupportedKeySizes();
-
- /**
- * @param <A> Appendable generic type
- * @param sb The {@link Appendable} instance to update
- * @param encContext
- * @return Same appendable instance
- * @throws IOException
- */
- <A extends Appendable> A appendPrivateKeyEncryptionContext(A sb, PrivateKeyEncryptionContext encContext) throws IOException;
-
- /**
- * @param encContext The encryption context
- * @return An initialization vector suitable to the specified context
- * @throws GeneralSecurityException
- */
- byte[] generateInitializationVector(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException;
-
- /**
- * @param bytes Original bytes
- * @param encContext The encryption context
- * @param encryptIt If {@code true} then encrypt the original bytes, otherwise decrypt them
- * @return The result of applying the cipher to the original bytes
- * @throws GeneralSecurityException If cannot encrypt/decrypt
- */
- byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
deleted file mode 100644
index 6188a04..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
+++ /dev/null
@@ -1,139 +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.loader.openssh;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.spec.DSAPrivateKeySpec;
-import java.security.spec.DSAPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Collections;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class OpenSSHDSSPrivateKeyEntryDecoder extends AbstractPrivateKeyEntryDecoder<DSAPublicKey, DSAPrivateKey> {
- public static final OpenSSHDSSPrivateKeyEntryDecoder INSTANCE = new OpenSSHDSSPrivateKeyEntryDecoder();
-
- public OpenSSHDSSPrivateKeyEntryDecoder() {
- super(DSAPublicKey.class, DSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_DSS)));
- }
-
- @Override
- public DSAPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
- throws IOException, GeneralSecurityException {
- if (!KeyPairProvider.SSH_DSS.equals(keyType)) { // just in case we were invoked directly
- throw new InvalidKeySpecException("Unexpected key type: " + keyType);
- }
-
- BigInteger p = KeyEntryResolver.decodeBigInt(keyData);
- BigInteger q = KeyEntryResolver.decodeBigInt(keyData);
- BigInteger g = KeyEntryResolver.decodeBigInt(keyData);
- BigInteger y = KeyEntryResolver.decodeBigInt(keyData);
- Objects.requireNonNull(y, "No public key data"); // TODO run some validation on it
- BigInteger x = KeyEntryResolver.decodeBigInt(keyData);
-
- return generatePrivateKey(new DSAPrivateKeySpec(x, p, q, g));
- }
-
- @Override
- public String encodePrivateKey(OutputStream s, DSAPrivateKey key) throws IOException {
- Objects.requireNonNull(key, "No private key provided");
-
- DSAParams keyParams = Objects.requireNonNull(key.getParams(), "No DSA params available");
- BigInteger p = keyParams.getP();
- KeyEntryResolver.encodeBigInt(s, p);
- KeyEntryResolver.encodeBigInt(s, keyParams.getQ());
-
- BigInteger g = keyParams.getG();
- KeyEntryResolver.encodeBigInt(s, g);
-
- BigInteger x = key.getX();
- BigInteger y = g.modPow(x, p);
- KeyEntryResolver.encodeBigInt(s, y);
- KeyEntryResolver.encodeBigInt(s, x);
- return KeyPairProvider.SSH_DSS;
- }
-
- @Override
- public boolean isPublicKeyRecoverySupported() {
- return true;
- }
-
- @Override
- public DSAPublicKey recoverPublicKey(DSAPrivateKey privateKey) throws GeneralSecurityException {
- return KeyUtils.recoverDSAPublicKey(privateKey);
- }
-
- @Override
- public DSAPublicKey clonePublicKey(DSAPublicKey key) throws GeneralSecurityException {
- if (key == null) {
- return null;
- }
-
- DSAParams params = key.getParams();
- if (params == null) {
- throw new InvalidKeyException("Missing parameters in key");
- }
-
- return generatePublicKey(new DSAPublicKeySpec(key.getY(), params.getP(), params.getQ(), params.getG()));
- }
-
- @Override
- public DSAPrivateKey clonePrivateKey(DSAPrivateKey key) throws GeneralSecurityException {
- if (key == null) {
- return null;
- }
-
- DSAParams params = key.getParams();
- if (params == null) {
- throw new InvalidKeyException("Missing parameters in key");
- }
-
- return generatePrivateKey(new DSAPrivateKeySpec(key.getX(), params.getP(), params.getQ(), params.getG()));
- }
-
- @Override
- public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
- return SecurityUtils.getKeyPairGenerator(KeyUtils.DSS_ALGORITHM);
- }
-
- @Override
- public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
- return SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
- }
-}
[43/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
new file mode 100644
index 0000000..95db256
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
@@ -0,0 +1,358 @@
+/*
+ * 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.loader.openssh;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.TreeMap;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Basic support for <A HREF="http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?rev=1.1&content-type=text/x-cvsweb-markup">OpenSSH key file(s)</A>
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser {
+ public static final String BEGIN_MARKER = "BEGIN OPENSSH PRIVATE KEY";
+ public static final List<String> BEGINNERS =
+ Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
+
+ public static final String END_MARKER = "END OPENSSH PRIVATE KEY";
+ public static final List<String> ENDERS =
+ Collections.unmodifiableList(Collections.singletonList(END_MARKER));
+
+ public static final String AUTH_MAGIC = "openssh-key-v1";
+ public static final OpenSSHKeyPairResourceParser INSTANCE = new OpenSSHKeyPairResourceParser();
+
+ private static final byte[] AUTH_MAGIC_BYTES = AUTH_MAGIC.getBytes(StandardCharsets.UTF_8);
+ private static final Map<String, PrivateKeyEntryDecoder<?, ?>> BY_KEY_TYPE_DECODERS_MAP =
+ new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+ private static final Map<Class<?>, PrivateKeyEntryDecoder<?, ?>> BY_KEY_CLASS_DECODERS_MAP =
+ new HashMap<>();
+
+ static {
+ registerPrivateKeyEntryDecoder(OpenSSHRSAPrivateKeyDecoder.INSTANCE);
+ registerPrivateKeyEntryDecoder(OpenSSHDSSPrivateKeyEntryDecoder.INSTANCE);
+
+ if (SecurityUtils.isECCSupported()) {
+ registerPrivateKeyEntryDecoder(OpenSSHECDSAPrivateKeyEntryDecoder.INSTANCE);
+ }
+ if (SecurityUtils.isEDDSACurveSupported()) {
+ registerPrivateKeyEntryDecoder(SecurityUtils.getOpenSSHEDDSAPrivateKeyEntryDecoder());
+ }
+ }
+
+ public OpenSSHKeyPairResourceParser() {
+ super(BEGINNERS, ENDERS);
+ }
+
+ @Override
+ public Collection<KeyPair> extractKeyPairs(
+ String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+ throws IOException, GeneralSecurityException {
+ stream = validateStreamMagicMarker(resourceKey, stream);
+
+ String cipher = KeyEntryResolver.decodeString(stream);
+ if (!OpenSSHParserContext.IS_NONE_CIPHER.test(cipher)) {
+ throw new NoSuchAlgorithmException("Unsupported cipher: " + cipher);
+ }
+
+ boolean debugEnabled = log.isDebugEnabled();
+ if (debugEnabled) {
+ log.debug("extractKeyPairs({}) cipher={}", resourceKey, cipher);
+ }
+
+ String kdfName = KeyEntryResolver.decodeString(stream);
+ if (!OpenSSHParserContext.IS_NONE_KDF.test(kdfName)) {
+ throw new NoSuchAlgorithmException("Unsupported KDF: " + kdfName);
+ }
+
+ byte[] kdfOptions = KeyEntryResolver.readRLEBytes(stream);
+ if (debugEnabled) {
+ log.debug("extractKeyPairs({}) KDF={}, options={}",
+ resourceKey, kdfName, BufferUtils.toHex(':', kdfOptions));
+ }
+
+ int numKeys = KeyEntryResolver.decodeInt(stream);
+ if (numKeys <= 0) {
+ if (debugEnabled) {
+ log.debug("extractKeyPairs({}) no encoded keys", resourceKey);
+ }
+ return Collections.emptyList();
+ }
+
+ List<PublicKey> publicKeys = new ArrayList<>(numKeys);
+ OpenSSHParserContext context = new OpenSSHParserContext(cipher, kdfName, kdfOptions);
+ boolean traceEnabled = log.isTraceEnabled();
+ for (int index = 1; index <= numKeys; index++) {
+ PublicKey pubKey = readPublicKey(resourceKey, context, stream);
+ ValidateUtils.checkNotNull(pubKey, "Empty public key #%d in %s", index, resourceKey);
+ if (traceEnabled) {
+ log.trace("extractKeyPairs({}) read public key #{}: {} {}",
+ resourceKey, index, KeyUtils.getKeyType(pubKey), KeyUtils.getFingerPrint(pubKey));
+ }
+ publicKeys.add(pubKey);
+ }
+
+ byte[] privateData = KeyEntryResolver.readRLEBytes(stream);
+ try (InputStream bais = new ByteArrayInputStream(privateData)) {
+ return readPrivateKeys(resourceKey, context, publicKeys, passwordProvider, bais);
+ }
+ }
+
+ protected PublicKey readPublicKey(
+ String resourceKey, OpenSSHParserContext context, InputStream stream)
+ throws IOException, GeneralSecurityException {
+ byte[] keyData = KeyEntryResolver.readRLEBytes(stream);
+ try (InputStream bais = new ByteArrayInputStream(keyData)) {
+ String keyType = KeyEntryResolver.decodeString(bais);
+ PublicKeyEntryDecoder<?, ?> decoder = KeyUtils.getPublicKeyEntryDecoder(keyType);
+ if (decoder == null) {
+ throw new NoSuchAlgorithmException("Unsupported key type (" + keyType + ") in " + resourceKey);
+ }
+
+ return decoder.decodePublicKey(keyType, bais);
+ }
+ }
+
+ protected List<KeyPair> readPrivateKeys(
+ String resourceKey, OpenSSHParserContext context, Collection<? extends PublicKey> publicKeys,
+ FilePasswordProvider passwordProvider, InputStream stream)
+ throws IOException, GeneralSecurityException {
+ if (GenericUtils.isEmpty(publicKeys)) {
+ return Collections.emptyList();
+ }
+
+ boolean traceEnabled = log.isTraceEnabled();
+ int check1 = KeyEntryResolver.decodeInt(stream);
+ int check2 = KeyEntryResolver.decodeInt(stream);
+ if (traceEnabled) {
+ log.trace("readPrivateKeys({}) check1=0x{}, check2=0x{}",
+ resourceKey, Integer.toHexString(check1), Integer.toHexString(check2));
+ }
+
+ List<KeyPair> keyPairs = new ArrayList<>(publicKeys.size());
+ for (PublicKey pubKey : publicKeys) {
+ String pubType = KeyUtils.getKeyType(pubKey);
+ int keyIndex = keyPairs.size() + 1;
+ if (traceEnabled) {
+ log.trace("extractKeyPairs({}) read private key #{}: {}",
+ resourceKey, keyIndex, pubType);
+ }
+
+ Map.Entry<PrivateKey, String> prvData = readPrivateKey(resourceKey, context, pubType, passwordProvider, stream);
+ PrivateKey prvKey = (prvData == null) ? null : prvData.getKey();
+ ValidateUtils.checkNotNull(prvKey, "Empty private key #%d in %s", keyIndex, resourceKey);
+
+ String prvType = KeyUtils.getKeyType(prvKey);
+ ValidateUtils.checkTrue(Objects.equals(pubType, prvType),
+ "Mismatched public (%s) vs. private (%s) key type #%d in %s",
+ pubType, prvType, keyIndex, resourceKey);
+
+ if (traceEnabled) {
+ log.trace("extractKeyPairs({}) add private key #{}: {} {}",
+ resourceKey, keyIndex, prvType, prvData.getValue());
+ }
+ keyPairs.add(new KeyPair(pubKey, prvKey));
+ }
+
+ return keyPairs;
+ }
+
+ protected SimpleImmutableEntry<PrivateKey, String> readPrivateKey(
+ String resourceKey, OpenSSHParserContext context, String keyType, FilePasswordProvider passwordProvider, InputStream stream)
+ throws IOException, GeneralSecurityException {
+ String prvType = KeyEntryResolver.decodeString(stream);
+ if (!Objects.equals(keyType, prvType)) {
+ throw new StreamCorruptedException("Mismatched private key type: "
+ + ", expected=" + keyType + ", actual=" + prvType
+ + " in " + resourceKey);
+ }
+
+ PrivateKeyEntryDecoder<?, ?> decoder = getPrivateKeyEntryDecoder(prvType);
+ if (decoder == null) {
+ throw new NoSuchAlgorithmException("Unsupported key type (" + prvType + ") in " + resourceKey);
+ }
+
+ PrivateKey prvKey = decoder.decodePrivateKey(prvType, passwordProvider, stream);
+ if (prvKey == null) {
+ throw new InvalidKeyException("Cannot parse key type (" + prvType + ") in " + resourceKey);
+ }
+
+ String comment = KeyEntryResolver.decodeString(stream);
+ return new SimpleImmutableEntry<>(prvKey, comment);
+ }
+
+ protected <S extends InputStream> S validateStreamMagicMarker(String resourceKey, S stream) throws IOException {
+ byte[] actual = new byte[AUTH_MAGIC_BYTES.length];
+ IoUtils.readFully(stream, actual);
+ if (!Arrays.equals(AUTH_MAGIC_BYTES, actual)) {
+ throw new StreamCorruptedException(resourceKey + ": Mismatched magic marker value: " + BufferUtils.toHex(':', actual));
+ }
+
+ int eos = stream.read();
+ if (eos == -1) {
+ throw new EOFException(resourceKey + ": Premature EOF after magic marker value");
+ }
+
+ if (eos != 0) {
+ throw new StreamCorruptedException(resourceKey + ": Missing EOS after magic marker value: 0x" + Integer.toHexString(eos));
+ }
+
+ return stream;
+ }
+
+ /**
+ * @param decoder The decoder to register
+ * @throws IllegalArgumentException if no decoder or not key type or no
+ * supported names for the decoder
+ * @see PrivateKeyEntryDecoder#getPublicKeyType()
+ * @see PrivateKeyEntryDecoder#getSupportedTypeNames()
+ */
+ public static void registerPrivateKeyEntryDecoder(PrivateKeyEntryDecoder<?, ?> decoder) {
+ Objects.requireNonNull(decoder, "No decoder specified");
+
+ Class<?> pubType = Objects.requireNonNull(decoder.getPublicKeyType(), "No public key type declared");
+ Class<?> prvType = Objects.requireNonNull(decoder.getPrivateKeyType(), "No private key type declared");
+ synchronized (BY_KEY_CLASS_DECODERS_MAP) {
+ BY_KEY_CLASS_DECODERS_MAP.put(pubType, decoder);
+ BY_KEY_CLASS_DECODERS_MAP.put(prvType, decoder);
+ }
+
+ Collection<String> names = ValidateUtils.checkNotNullAndNotEmpty(decoder.getSupportedTypeNames(), "No supported key type");
+ synchronized (BY_KEY_TYPE_DECODERS_MAP) {
+ for (String n : names) {
+ PrivateKeyEntryDecoder<?, ?> prev = BY_KEY_TYPE_DECODERS_MAP.put(n, decoder);
+ if (prev != null) {
+ //noinspection UnnecessaryContinue
+ continue; // debug breakpoint
+ }
+ }
+ }
+ }
+
+ /**
+ * @param keyType The {@code OpenSSH} key type string - e.g., {@code ssh-rsa, ssh-dss}
+ * - ignored if {@code null}/empty
+ * @return The registered {@link PrivateKeyEntryDecoder} or {code null} if not found
+ */
+ public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(String keyType) {
+ if (GenericUtils.isEmpty(keyType)) {
+ return null;
+ }
+
+ synchronized (BY_KEY_TYPE_DECODERS_MAP) {
+ return BY_KEY_TYPE_DECODERS_MAP.get(keyType);
+ }
+ }
+
+ /**
+ * @param kp The {@link KeyPair} to examine - ignored if {@code null}
+ * @return The matching {@link PrivateKeyEntryDecoder} provided <U>both</U>
+ * the public and private keys have the same decoder - {@code null} if no
+ * match found
+ * @see #getPrivateKeyEntryDecoder(Key)
+ */
+ public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(KeyPair kp) {
+ if (kp == null) {
+ return null;
+ }
+
+ PrivateKeyEntryDecoder<?, ?> d1 = getPrivateKeyEntryDecoder(kp.getPublic());
+ PrivateKeyEntryDecoder<?, ?> d2 = getPrivateKeyEntryDecoder(kp.getPrivate());
+ if (d1 == d2) {
+ return d1;
+ } else {
+ return null; // some kind of mixed keys...
+ }
+ }
+
+ /**
+ * @param key The {@link Key} (public or private) - ignored if {@code null}
+ * @return The registered {@link PrivateKeyEntryDecoder} for this key or {code null} if no match found
+ * @see #getPrivateKeyEntryDecoder(Class)
+ */
+ public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(Key key) {
+ if (key == null) {
+ return null;
+ } else {
+ return getPrivateKeyEntryDecoder(key.getClass());
+ }
+ }
+
+ /**
+ * @param keyType The key {@link Class} - ignored if {@code null} or not a {@link Key}
+ * compatible type
+ * @return The registered {@link PrivateKeyEntryDecoder} or {code null} if no match found
+ */
+ public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(Class<?> keyType) {
+ if ((keyType == null) || (!Key.class.isAssignableFrom(keyType))) {
+ return null;
+ }
+
+ synchronized (BY_KEY_TYPE_DECODERS_MAP) {
+ PrivateKeyEntryDecoder<?, ?> decoder = BY_KEY_CLASS_DECODERS_MAP.get(keyType);
+ if (decoder != null) {
+ return decoder;
+ }
+
+ // in case it is a derived class
+ for (PrivateKeyEntryDecoder<?, ?> dec : BY_KEY_CLASS_DECODERS_MAP.values()) {
+ Class<?> pubType = dec.getPublicKeyType();
+ Class<?> prvType = dec.getPrivateKeyType();
+ if (pubType.isAssignableFrom(keyType) || prvType.isAssignableFrom(keyType)) {
+ return dec;
+ }
+ }
+ }
+
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHParserContext.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHParserContext.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHParserContext.java
new file mode 100644
index 0000000..07f2a9a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHParserContext.java
@@ -0,0 +1,83 @@
+/*
+ * 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.loader.openssh;
+
+import java.util.function.Predicate;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class OpenSSHParserContext {
+ public static final String NONE_CIPHER = "none";
+ public static final Predicate<String> IS_NONE_CIPHER = c -> GenericUtils.isEmpty(c) || NONE_CIPHER.equalsIgnoreCase(c);
+
+ public static final String NONE_KDF = "none";
+ public static final Predicate<String> IS_NONE_KDF = c -> GenericUtils.isEmpty(c) || NONE_KDF.equalsIgnoreCase(c);
+
+ private String cipherName;
+ private String kdfName;
+ private byte[] kdfOptions;
+
+ public OpenSSHParserContext() {
+ super();
+ }
+
+ public OpenSSHParserContext(String cipherName, String kdfName, byte... kdfOptions) {
+ this.cipherName = cipherName;
+ this.kdfName = kdfName;
+ this.kdfOptions = kdfOptions;
+ }
+
+ public String getCipherName() {
+ return cipherName;
+ }
+
+ public void setCipherName(String cipherName) {
+ this.cipherName = cipherName;
+ }
+
+ public String getKdfName() {
+ return kdfName;
+ }
+
+ public void setKdfName(String kdfName) {
+ this.kdfName = kdfName;
+ }
+
+ public byte[] getKdfOptions() {
+ return kdfOptions;
+ }
+
+ public void setKdfOptions(byte[] kdfOptions) {
+ this.kdfOptions = kdfOptions;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName()
+ + "[cipher=" + getCipherName()
+ + ", kdfName=" + getKdfName()
+ + ", kdfOptions=" + BufferUtils.toHex(':', getKdfOptions())
+ + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
new file mode 100644
index 0000000..72e003f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
@@ -0,0 +1,135 @@
+/*
+ * 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.loader.openssh;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class OpenSSHRSAPrivateKeyDecoder extends AbstractPrivateKeyEntryDecoder<RSAPublicKey, RSAPrivateKey> {
+ public static final BigInteger DEFAULT_PUBLIC_EXPONENT = new BigInteger("65537");
+ public static final OpenSSHRSAPrivateKeyDecoder INSTANCE = new OpenSSHRSAPrivateKeyDecoder();
+
+ public OpenSSHRSAPrivateKeyDecoder() {
+ super(RSAPublicKey.class, RSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_RSA)));
+ }
+
+ @Override
+ public RSAPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
+ throws IOException, GeneralSecurityException {
+ if (!KeyPairProvider.SSH_RSA.equals(keyType)) { // just in case we were invoked directly
+ throw new InvalidKeySpecException("Unexpected key type: " + keyType);
+ }
+
+ BigInteger n = KeyEntryResolver.decodeBigInt(keyData);
+ BigInteger e = KeyEntryResolver.decodeBigInt(keyData);
+ if (!Objects.equals(e, DEFAULT_PUBLIC_EXPONENT)) {
+ log.warn("decodePrivateKey({}) non-standard RSA exponent found: {}", keyType, e);
+ }
+
+ BigInteger d = KeyEntryResolver.decodeBigInt(keyData);
+ BigInteger inverseQmodP = KeyEntryResolver.decodeBigInt(keyData);
+ Objects.requireNonNull(inverseQmodP, "Missing iqmodp"); // TODO run some validation on it
+ BigInteger p = KeyEntryResolver.decodeBigInt(keyData);
+ BigInteger q = KeyEntryResolver.decodeBigInt(keyData);
+ BigInteger modulus = p.multiply(q);
+ if (!Objects.equals(n, modulus)) {
+ log.warn("decodePrivateKey({}) mismatched modulus values: encoded={}, calculated={}",
+ keyType, n, modulus);
+ }
+
+ return generatePrivateKey(new RSAPrivateKeySpec(n, d));
+ }
+
+ @Override
+ public boolean isPublicKeyRecoverySupported() {
+ return true;
+ }
+
+ @Override
+ public RSAPublicKey recoverPublicKey(RSAPrivateKey privateKey) throws GeneralSecurityException {
+ return KeyUtils.recoverRSAPublicKey(privateKey);
+ }
+
+ @Override
+ public RSAPublicKey clonePublicKey(RSAPublicKey key) throws GeneralSecurityException {
+ if (key == null) {
+ return null;
+ } else {
+ return generatePublicKey(new RSAPublicKeySpec(key.getModulus(), key.getPublicExponent()));
+ }
+ }
+
+ @Override
+ public RSAPrivateKey clonePrivateKey(RSAPrivateKey key) throws GeneralSecurityException {
+ if (key == null) {
+ return null;
+ }
+
+ if (!(key instanceof RSAPrivateCrtKey)) {
+ throw new InvalidKeyException("Cannot clone a non-RSAPrivateCrtKey: " + key.getClass().getSimpleName());
+ }
+
+ RSAPrivateCrtKey rsaPrv = (RSAPrivateCrtKey) key;
+ return generatePrivateKey(
+ new RSAPrivateCrtKeySpec(
+ rsaPrv.getModulus(),
+ rsaPrv.getPublicExponent(),
+ rsaPrv.getPrivateExponent(),
+ rsaPrv.getPrimeP(),
+ rsaPrv.getPrimeQ(),
+ rsaPrv.getPrimeExponentP(),
+ rsaPrv.getPrimeExponentQ(),
+ rsaPrv.getCrtCoefficient()));
+ }
+
+ @Override
+ public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+ return SecurityUtils.getKeyPairGenerator(KeyUtils.RSA_ALGORITHM);
+ }
+
+ @Override
+ public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+ return SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
new file mode 100644
index 0000000..bee13d6
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
@@ -0,0 +1,167 @@
+/*
+ * 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.loader.pem;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import javax.security.auth.login.CredentialException;
+import javax.security.auth.login.FailedLoginException;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+import org.apache.sshd.common.config.keys.loader.PrivateKeyEncryptionContext;
+import org.apache.sshd.common.config.keys.loader.PrivateKeyObfuscator;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+
+/**
+ * Base class for PEM file key-pair loaders
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractPEMResourceKeyPairParser
+ extends AbstractKeyPairResourceParser
+ implements KeyPairPEMResourceParser {
+ private final String algo;
+ private final String algId;
+
+ protected AbstractPEMResourceKeyPairParser(String algo, String algId, List<String> beginners, List<String> enders) {
+ super(beginners, enders);
+ this.algo = ValidateUtils.checkNotNullAndNotEmpty(algo, "No encryption algorithm provided");
+ this.algId = ValidateUtils.checkNotNullAndNotEmpty(algId, "No algorithm identifier provided");
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return algo;
+ }
+
+ @Override
+ public String getAlgorithmIdentifier() {
+ return algId;
+ }
+
+ @Override
+ public Collection<KeyPair> extractKeyPairs(
+ String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
+ throws IOException, GeneralSecurityException {
+ if (GenericUtils.isEmpty(lines)) {
+ return Collections.emptyList();
+ }
+
+ Boolean encrypted = null;
+ byte[] initVector = null;
+ String algInfo = null;
+ int dataStartIndex = -1;
+ for (int index = 0; index < lines.size(); index++) {
+ String line = GenericUtils.trimToEmpty(lines.get(index));
+ if (GenericUtils.isEmpty(line)) {
+ continue;
+ }
+
+ // check if header line - if not, assume data lines follow
+ int headerPos = line.indexOf(':');
+ if (headerPos < 0) {
+ dataStartIndex = index;
+ break;
+ }
+
+ if (line.startsWith("Proc-Type:")) {
+ if (encrypted != null) {
+ throw new StreamCorruptedException("Multiple encryption indicators in " + resourceKey);
+ }
+
+ line = line.substring(headerPos + 1).trim();
+ line = line.toUpperCase();
+ encrypted = Boolean.valueOf(line.contains("ENCRYPTED"));
+ } else if (line.startsWith("DEK-Info:")) {
+ if ((initVector != null) || (algInfo != null)) {
+ throw new StreamCorruptedException("Multiple encryption settings in " + resourceKey);
+ }
+
+ line = line.substring(headerPos + 1).trim();
+ headerPos = line.indexOf(',');
+ if (headerPos < 0) {
+ throw new StreamCorruptedException(resourceKey + ": Missing encryption data values separator in line '" + line + "'");
+ }
+
+ algInfo = line.substring(0, headerPos).trim();
+
+ String algInitVector = line.substring(headerPos + 1).trim();
+ initVector = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, algInitVector);
+ }
+ }
+
+ if (dataStartIndex < 0) {
+ throw new StreamCorruptedException("No data lines (only headers or empty) found in " + resourceKey);
+ }
+
+ List<String> dataLines = lines.subList(dataStartIndex, lines.size());
+ if ((encrypted != null) || (algInfo != null) || (initVector != null)) {
+ if (passwordProvider == null) {
+ throw new CredentialException("Missing password provider for encrypted resource=" + resourceKey);
+ }
+
+ String password = passwordProvider.getPassword(resourceKey);
+ if (GenericUtils.isEmpty(password)) {
+ throw new FailedLoginException("No password data for encrypted resource=" + resourceKey);
+ }
+
+ PrivateKeyEncryptionContext encContext = new PrivateKeyEncryptionContext(algInfo);
+ encContext.setPassword(password);
+ encContext.setInitVector(initVector);
+ byte[] encryptedData = KeyPairResourceParser.extractDataBytes(dataLines);
+ byte[] decodedData = applyPrivateKeyCipher(encryptedData, encContext, false);
+ try (InputStream bais = new ByteArrayInputStream(decodedData)) {
+ return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, bais);
+ }
+ }
+
+ return super.extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, dataLines);
+ }
+
+ protected byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException {
+ String cipherName = encContext.getCipherName();
+ PrivateKeyObfuscator o = encContext.resolvePrivateKeyObfuscator();
+ if (o == null) {
+ throw new NoSuchAlgorithmException("decryptPrivateKeyData(" + encContext + ")[encrypt=" + encryptIt + "] unknown cipher: " + cipherName);
+ }
+
+ if (encryptIt) {
+ byte[] initVector = encContext.getInitVector();
+ if (GenericUtils.isEmpty(initVector)) {
+ initVector = o.generateInitializationVector(encContext);
+ encContext.setInitVector(initVector);
+ }
+ }
+
+ return o.applyPrivateKeyCipher(bytes, encContext, encryptIt);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
new file mode 100644
index 0000000..0c357af
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
@@ -0,0 +1,126 @@
+/*
+ * 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.loader.pem;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.der.ASN1Object;
+import org.apache.sshd.common.util.io.der.ASN1Type;
+import org.apache.sshd.common.util.io.der.DERParser;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DSSPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParser {
+ // Not exactly according to standard but good enough
+ public static final String BEGIN_MARKER = "BEGIN DSA PRIVATE KEY";
+ public static final List<String> BEGINNERS =
+ Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
+
+ public static final String END_MARKER = "END DSA PRIVATE KEY";
+ public static final List<String> ENDERS =
+ Collections.unmodifiableList(Collections.singletonList(END_MARKER));
+
+ /**
+ * @see <A HREF="https://tools.ietf.org/html/rfc3279#section-2.3.2">RFC-3279 section 2.3.2</A>
+ */
+ public static final String DSS_OID = "1.2.840.10040.4.1";
+
+ public static final DSSPEMResourceKeyPairParser INSTANCE = new DSSPEMResourceKeyPairParser();
+
+ public DSSPEMResourceKeyPairParser() {
+ super(KeyUtils.DSS_ALGORITHM, DSS_OID, BEGINNERS, ENDERS);
+ }
+
+ @Override
+ public Collection<KeyPair> extractKeyPairs(
+ String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+ throws IOException, GeneralSecurityException {
+ KeyPair kp = decodeDSSKeyPair(SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM), stream, false);
+ return Collections.singletonList(kp);
+ }
+
+ /**
+ * <p>The ASN.1 syntax for the private key:</P>
+ * <pre><code>
+ * DSAPrivateKey ::= SEQUENCE {
+ * version Version,
+ * p INTEGER,
+ * q INTEGER,
+ * g INTEGER,
+ * y INTEGER,
+ * x INTEGER
+ * }
+ * </code></pre>
+ * @param kf The {@link KeyFactory} To use to generate the keys
+ * @param s The {@link InputStream} containing the encoded bytes
+ * @param okToClose <code>true</code> if the method may close the input
+ * stream regardless of success or failure
+ * @return The recovered {@link KeyPair}
+ * @throws IOException If failed to read or decode the bytes
+ * @throws GeneralSecurityException If failed to generate the keys
+ */
+ public static KeyPair decodeDSSKeyPair(KeyFactory kf, InputStream s, boolean okToClose)
+ throws IOException, GeneralSecurityException {
+ ASN1Object sequence;
+ try (DERParser parser = new DERParser(NoCloseInputStream.resolveInputStream(s, okToClose))) {
+ sequence = parser.readObject();
+ }
+
+ if (!ASN1Type.SEQUENCE.equals(sequence.getObjType())) {
+ throw new IOException("Invalid DER: not a sequence: " + sequence.getObjType());
+ }
+
+ // Parse inside the sequence
+ try (DERParser parser = sequence.createParser()) {
+ // Skip version
+ ASN1Object version = parser.readObject();
+ if (version == null) {
+ throw new StreamCorruptedException("No version");
+ }
+
+ BigInteger p = parser.readObject().asInteger();
+ BigInteger q = parser.readObject().asInteger();
+ BigInteger g = parser.readObject().asInteger();
+ BigInteger y = parser.readObject().asInteger();
+ BigInteger x = parser.readObject().asInteger();
+ PublicKey pubKey = kf.generatePublic(new DSAPublicKeySpec(y, p, q, g));
+ PrivateKey prvKey = kf.generatePrivate(new DSAPrivateKeySpec(x, p, q, g));
+ return new KeyPair(pubKey, prvKey);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
new file mode 100644
index 0000000..dd8d2ea
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
@@ -0,0 +1,220 @@
+/*
+ * 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.loader.pem;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchProviderException;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.der.ASN1Object;
+import org.apache.sshd.common.util.io.der.ASN1Type;
+import org.apache.sshd.common.util.io.der.DERParser;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParser {
+ public static final String BEGIN_MARKER = "BEGIN EC PRIVATE KEY";
+ public static final List<String> BEGINNERS =
+ Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
+
+ public static final String END_MARKER = "END EC PRIVATE KEY";
+ public static final List<String> ENDERS =
+ Collections.unmodifiableList(Collections.singletonList(END_MARKER));
+
+ /**
+ * @see <A HREF="https://tools.ietf.org/html/rfc3279#section-2.3.5">RFC-3279 section 2.3.5</A>
+ */
+ public static final String ECDSA_OID = "1.2.840.10045.2.1";
+
+ public static final ECDSAPEMResourceKeyPairParser INSTANCE = new ECDSAPEMResourceKeyPairParser();
+
+ public ECDSAPEMResourceKeyPairParser() {
+ super(KeyUtils.EC_ALGORITHM, ECDSA_OID, BEGINNERS, ENDERS);
+ }
+
+ @Override
+ public Collection<KeyPair> extractKeyPairs(
+ String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+ throws IOException, GeneralSecurityException {
+ Map.Entry<ECPublicKeySpec, ECPrivateKeySpec> spec = decodeECPrivateKeySpec(stream, false);
+ if (!SecurityUtils.isECCSupported()) {
+ throw new NoSuchProviderException("ECC not supported");
+ }
+
+ KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
+ ECPublicKey pubKey = (ECPublicKey) kf.generatePublic(spec.getKey());
+ ECPrivateKey prvKey = (ECPrivateKey) kf.generatePrivate(spec.getValue());
+ KeyPair kp = new KeyPair(pubKey, prvKey);
+ return Collections.singletonList(kp);
+ }
+
+ /**
+ * <P>ASN.1 syntax according to rfc5915 is:</P></BR>
+ * <PRE><CODE>
+ * ECPrivateKey ::= SEQUENCE {
+ * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
+ * privateKey OCTET STRING,
+ * parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
+ * publicKey [1] BIT STRING OPTIONAL
+ * }
+ * </CODE></PRE>
+ * <P><I>ECParameters</I> syntax according to RFC5480:</P></BR>
+ * <PRE><CODE>
+ * ECParameters ::= CHOICE {
+ * namedCurve OBJECT IDENTIFIER
+ * -- implicitCurve NULL
+ * -- specifiedCurve SpecifiedECDomain
+ * }
+ * </CODE></PRE>
+ * @param inputStream The {@link InputStream} containing the DER encoded data
+ * @param okToClose {@code true} if OK to close the DER stream once parsing complete
+ * @return The decoded {@link SimpleImmutableEntry} of {@link ECPublicKeySpec} and {@link ECPrivateKeySpec}
+ * @throws IOException If failed to to decode the DER stream
+ */
+ public static SimpleImmutableEntry<ECPublicKeySpec, ECPrivateKeySpec> decodeECPrivateKeySpec(InputStream inputStream, boolean okToClose) throws IOException {
+ ASN1Object sequence;
+ try (DERParser parser = new DERParser(NoCloseInputStream.resolveInputStream(inputStream, okToClose))) {
+ sequence = parser.readObject();
+ }
+
+ if (!ASN1Type.SEQUENCE.equals(sequence.getObjType())) {
+ throw new IOException("Invalid DER: not a sequence: " + sequence.getObjType());
+ }
+
+ // Parse inside the sequence
+ try (DERParser parser = sequence.createParser()) {
+ ECPrivateKeySpec prvSpec = decodeECPrivateKeySpec(parser);
+ ECCurves curve = ECCurves.fromCurveParameters(prvSpec.getParams());
+ if (curve == null) {
+ throw new StreamCorruptedException("Unknown curve");
+ }
+
+ ECPoint w = decodeECPublicKeyValue(curve, parser);
+ ECPublicKeySpec pubSpec = new ECPublicKeySpec(w, prvSpec.getParams());
+ return new SimpleImmutableEntry<>(pubSpec, prvSpec);
+ }
+ }
+
+ public static final ECPrivateKeySpec decodeECPrivateKeySpec(DERParser parser) throws IOException {
+ // see openssl asn1parse -inform PEM -in ...file... -dump
+ ASN1Object versionObject = parser.readObject(); // Skip version
+ if (versionObject == null) {
+ throw new StreamCorruptedException("No version");
+ }
+
+ // as per RFC-5915 section 3
+ BigInteger version = versionObject.asInteger();
+ if (!BigInteger.ONE.equals(version)) {
+ throw new StreamCorruptedException("Bad version value: " + version);
+ }
+
+ ASN1Object keyObject = parser.readObject();
+ if (keyObject == null) {
+ throw new StreamCorruptedException("No private key value");
+ }
+
+ ASN1Type objType = keyObject.getObjType();
+ if (!ASN1Type.OCTET_STRING.equals(objType)) {
+ throw new StreamCorruptedException("Non-matching private key object type: " + objType);
+ }
+
+ ASN1Object paramsObject = parser.readObject();
+ if (paramsObject == null) {
+ throw new StreamCorruptedException("No parameters value");
+ }
+
+ // TODO make sure params object tag is 0xA0
+
+ final List<Integer> curveOID;
+ try (DERParser paramsParser = paramsObject.createParser()) {
+ ASN1Object namedCurve = paramsParser.readObject();
+ if (namedCurve == null) {
+ throw new StreamCorruptedException("Missing named curve parameter");
+ }
+
+ curveOID = namedCurve.asOID();
+ }
+
+ ECCurves curve = ECCurves.fromOIDValue(curveOID);
+ if (curve == null) {
+ throw new StreamCorruptedException("Unknown curve OID: " + curveOID);
+ }
+
+ BigInteger s = ECCurves.octetStringToInteger(keyObject.getPureValueBytes());
+ return new ECPrivateKeySpec(s, curve.getParameters());
+ }
+
+ /**
+ * <P>ASN.1 syntax according to rfc5915 is:</P></BR>
+ * <PRE>
+ * publicKey [1] BIT STRING OPTIONAL
+ * </PRE>
+ * @param curve The {@link ECCurves} curve
+ * @param parser The {@link DERParser} assumed to be positioned at the
+ * start of the data
+ * @return The encoded {@link ECPoint}
+ * @throws IOException If failed to create the point
+ */
+ public static final ECPoint decodeECPublicKeyValue(ECCurves curve, DERParser parser) throws IOException {
+ // see openssl asn1parse -inform PEM -in ...file... -dump
+ ASN1Object dataObject = parser.readObject();
+ if (dataObject == null) {
+ throw new StreamCorruptedException("No public key data bytes");
+ }
+
+ try (DERParser dataParser = dataObject.createParser()) {
+ ASN1Object pointData = dataParser.readObject();
+ if (pointData == null) {
+ throw new StreamCorruptedException("Missing public key data parameter");
+ }
+
+ ASN1Type objType = pointData.getObjType();
+ if (!ASN1Type.BIT_STRING.equals(objType)) {
+ throw new StreamCorruptedException("Non-matching public key object type: " + objType);
+ }
+
+ // see https://tools.ietf.org/html/rfc5480#section-2.2
+ byte[] octets = pointData.getValue();
+ return ECCurves.octetStringToEcPoint(octets);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/KeyPairPEMResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/KeyPairPEMResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/KeyPairPEMResourceParser.java
new file mode 100644
index 0000000..07c7e44
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/KeyPairPEMResourceParser.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.pem;
+
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface KeyPairPEMResourceParser extends KeyPairResourceParser {
+ /**
+ * @return The encryption algorithm name - e.g., "RSA", "DSA"
+ */
+ String getAlgorithm();
+
+ /**
+ * @return The OID used to identify this algorithm in DER encodings - e.g., RSA=1.2.840.113549.1.1.1
+ */
+ String getAlgorithmIdentifier();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java
new file mode 100644
index 0000000..b6749da
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java
@@ -0,0 +1,109 @@
+/*
+ * 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.loader.pem;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+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 PEMResourceParserUtils {
+ public static final KeyPairResourceParser PROXY = new KeyPairResourceParser() {
+ @Override
+ public Collection<KeyPair> loadKeyPairs(
+ String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+ throws IOException, GeneralSecurityException {
+ @SuppressWarnings("synthetic-access")
+ KeyPairResourceParser proxy = PROXY_HOLDER.get();
+ return (proxy == null) ? Collections.<KeyPair>emptyList() : proxy.loadKeyPairs(resourceKey, passwordProvider, lines);
+ }
+
+ @Override
+ public boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+ throws IOException, GeneralSecurityException {
+ @SuppressWarnings("synthetic-access")
+ KeyPairResourceParser proxy = PROXY_HOLDER.get();
+ return (proxy != null) && proxy.canExtractKeyPairs(resourceKey, lines);
+ }
+ };
+
+ private static final Map<String, KeyPairPEMResourceParser> BY_OID_MAP = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ private static final Map<String, KeyPairPEMResourceParser> BY_ALGORITHM_MAP = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ private static final AtomicReference<KeyPairResourceParser> PROXY_HOLDER = new AtomicReference<>(KeyPairResourceParser.EMPTY);
+
+ static {
+ registerPEMResourceParser(RSAPEMResourceKeyPairParser.INSTANCE);
+ registerPEMResourceParser(DSSPEMResourceKeyPairParser.INSTANCE);
+ registerPEMResourceParser(ECDSAPEMResourceKeyPairParser.INSTANCE);
+ registerPEMResourceParser(PKCS8PEMResourceKeyPairParser.INSTANCE);
+ }
+
+ private PEMResourceParserUtils() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ public static void registerPEMResourceParser(KeyPairPEMResourceParser parser) {
+ Objects.requireNonNull(parser, "No parser to register");
+ synchronized (BY_OID_MAP) {
+ BY_OID_MAP.put(ValidateUtils.checkNotNullAndNotEmpty(parser.getAlgorithmIdentifier(), "No OID value"), parser);
+ }
+
+ synchronized (BY_ALGORITHM_MAP) {
+ BY_ALGORITHM_MAP.put(ValidateUtils.checkNotNullAndNotEmpty(parser.getAlgorithm(), "No algorithm value"), parser);
+ // Use a copy in order to avoid concurrent modifications
+ PROXY_HOLDER.set(KeyPairResourceParser.aggregate(new ArrayList<>(BY_ALGORITHM_MAP.values())));
+ }
+ }
+
+ public static KeyPairPEMResourceParser getPEMResourceParserByOid(String oid) {
+ if (GenericUtils.isEmpty(oid)) {
+ return null;
+ }
+
+ synchronized (BY_OID_MAP) {
+ return BY_OID_MAP.get(oid);
+ }
+ }
+
+ public static KeyPairPEMResourceParser getPEMResourceParserByAlgorithm(String algorithm) {
+ if (GenericUtils.isEmpty(algorithm)) {
+ return null;
+ }
+
+ synchronized (BY_ALGORITHM_MAP) {
+ return BY_ALGORITHM_MAP.get(algorithm);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
new file mode 100644
index 0000000..b333f23
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
@@ -0,0 +1,156 @@
+/*
+ * 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.loader.pem;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+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.der.ASN1Object;
+import org.apache.sshd.common.util.io.der.DERParser;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class PKCS8PEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParser {
+ // Not exactly according to standard but good enough
+ public static final String BEGIN_MARKER = "BEGIN PRIVATE KEY";
+ public static final List<String> BEGINNERS =
+ Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
+
+ public static final String END_MARKER = "END PRIVATE KEY";
+ public static final List<String> ENDERS =
+ Collections.unmodifiableList(Collections.singletonList(END_MARKER));
+
+ public static final String PKCS8_FORMAT = "PKCS#8";
+
+ public static final PKCS8PEMResourceKeyPairParser INSTANCE = new PKCS8PEMResourceKeyPairParser();
+
+ public PKCS8PEMResourceKeyPairParser() {
+ super(PKCS8_FORMAT, PKCS8_FORMAT, BEGINNERS, ENDERS);
+ }
+
+ @Override
+ public Collection<KeyPair> extractKeyPairs(
+ String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+ throws IOException, GeneralSecurityException {
+ // Save the data before getting the algorithm OID since we will need it
+ byte[] encBytes = IoUtils.toByteArray(stream);
+ List<Integer> oidAlgorithm = getPKCS8AlgorithmIdentifier(encBytes);
+ PrivateKey prvKey = decodePEMPrivateKeyPKCS8(oidAlgorithm, encBytes, passwordProvider);
+ PublicKey pubKey = ValidateUtils.checkNotNull(KeyUtils.recoverPublicKey(prvKey),
+ "Failed to recover public key of OID=%s", oidAlgorithm);
+ KeyPair kp = new KeyPair(pubKey, prvKey);
+ return Collections.singletonList(kp);
+ }
+
+ public static PrivateKey decodePEMPrivateKeyPKCS8(
+ List<Integer> oidAlgorithm, byte[] keyBytes, FilePasswordProvider passwordProvider)
+ throws GeneralSecurityException {
+ ValidateUtils.checkNotNullAndNotEmpty(oidAlgorithm, "No PKCS8 algorithm OID");
+ return decodePEMPrivateKeyPKCS8(GenericUtils.join(oidAlgorithm, '.'), keyBytes, passwordProvider);
+ }
+
+ public static PrivateKey decodePEMPrivateKeyPKCS8(
+ String oid, byte[] keyBytes, FilePasswordProvider passwordProvider)
+ throws GeneralSecurityException {
+ KeyPairPEMResourceParser parser =
+ PEMResourceParserUtils.getPEMResourceParserByOid(
+ ValidateUtils.checkNotNullAndNotEmpty(oid, "No PKCS8 algorithm OID"));
+ if (parser == null) {
+ throw new NoSuchAlgorithmException("decodePEMPrivateKeyPKCS8(" + oid + ") unknown algorithm identifier");
+ }
+
+ String algorithm = ValidateUtils.checkNotNullAndNotEmpty(parser.getAlgorithm(), "No parser algorithm");
+ KeyFactory factory = SecurityUtils.getKeyFactory(algorithm);
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
+ return factory.generatePrivate(keySpec);
+ }
+
+ public static List<Integer> getPKCS8AlgorithmIdentifier(byte[] input) throws IOException {
+ try (DERParser parser = new DERParser(input)) {
+ return getPKCS8AlgorithmIdentifier(parser);
+ }
+ }
+
+ /**
+ * According to the standard:
+ * <PRE><CODE>
+ * PrivateKeyInfo ::= SEQUENCE {
+ * version Version,
+ * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ * privateKey PrivateKey,
+ * attributes [0] IMPLICIT Attributes OPTIONAL
+ * }
+ *
+ * Version ::= INTEGER
+ * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
+ * PrivateKey ::= OCTET STRING
+ * Attributes ::= SET OF Attribute
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ * </CODE></PRE>
+ * @param parser The {@link DERParser} to use
+ * @return The PKCS8 algorithm OID
+ * @throws IOException If malformed data
+ * @see #getPKCS8AlgorithmIdentifier(ASN1Object)
+ */
+ public static List<Integer> getPKCS8AlgorithmIdentifier(DERParser parser) throws IOException {
+ return getPKCS8AlgorithmIdentifier(parser.readObject());
+ }
+
+ public static List<Integer> getPKCS8AlgorithmIdentifier(ASN1Object privateKeyInfo) throws IOException {
+ try (DERParser parser = privateKeyInfo.createParser()) {
+ // Skip version
+ ASN1Object versionObject = parser.readObject();
+ if (versionObject == null) {
+ throw new StreamCorruptedException("No version");
+ }
+
+ ASN1Object privateKeyAlgorithm = parser.readObject();
+ if (privateKeyAlgorithm == null) {
+ throw new StreamCorruptedException("No private key algorithm");
+ }
+
+ try (DERParser oidParser = privateKeyAlgorithm.createParser()) {
+ ASN1Object oid = oidParser.readObject();
+ return oid.asOID();
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
new file mode 100644
index 0000000..d760aaf
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
@@ -0,0 +1,142 @@
+/*
+ * 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.loader.pem;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.apache.sshd.common.util.io.der.ASN1Object;
+import org.apache.sshd.common.util.io.der.ASN1Type;
+import org.apache.sshd.common.util.io.der.DERParser;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class RSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParser {
+ // Not exactly according to standard but good enough
+ public static final String BEGIN_MARKER = "BEGIN RSA PRIVATE KEY";
+ public static final List<String> BEGINNERS =
+ Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
+
+ public static final String END_MARKER = "END RSA PRIVATE KEY";
+ public static final List<String> ENDERS =
+ Collections.unmodifiableList(Collections.singletonList(END_MARKER));
+
+ /**
+ * @see <A HREF="https://tools.ietf.org/html/rfc3279#section-2.3.1">RFC-3279 section 2.3.1</A>
+ */
+ public static final String RSA_OID = "1.2.840.113549.1.1.1";
+
+ public static final RSAPEMResourceKeyPairParser INSTANCE = new RSAPEMResourceKeyPairParser();
+
+ public RSAPEMResourceKeyPairParser() {
+ super(KeyUtils.RSA_ALGORITHM, RSA_OID, BEGINNERS, ENDERS);
+ }
+
+ @Override
+ public Collection<KeyPair> extractKeyPairs(
+ String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+ throws IOException, GeneralSecurityException {
+ KeyPair kp = decodeRSAKeyPair(SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM), stream, false);
+ return Collections.singletonList(kp);
+ }
+
+ /**
+ * <p>The ASN.1 syntax for the private key as per RFC-3447 section A.1.2:</P>
+ * <pre><code>
+ * RSAPrivateKey ::= SEQUENCE {
+ * version Version,
+ * modulus INTEGER, -- n
+ * publicExponent INTEGER, -- e
+ * privateExponent INTEGER, -- d
+ * prime1 INTEGER, -- p
+ * prime2 INTEGER, -- q
+ * exponent1 INTEGER, -- d mod (p-1)
+ * exponent2 INTEGER, -- d mod (q-1)
+ * coefficient INTEGER, -- (inverse of q) mod p
+ * otherPrimeInfos OtherPrimeInfos OPTIONAL
+ * }
+ * </code></pre>
+ * @param kf The {@link KeyFactory} To use to generate the keys
+ * @param s The {@link InputStream} containing the encoded bytes
+ * @param okToClose <code>true</code> if the method may close the input
+ * stream regardless of success or failure
+ * @return The recovered {@link KeyPair}
+ * @throws IOException If failed to read or decode the bytes
+ * @throws GeneralSecurityException If failed to generate the keys
+ */
+ public static KeyPair decodeRSAKeyPair(KeyFactory kf, InputStream s, boolean okToClose)
+ throws IOException, GeneralSecurityException {
+ ASN1Object sequence;
+ try (DERParser parser = new DERParser(NoCloseInputStream.resolveInputStream(s, okToClose))) {
+ sequence = parser.readObject();
+ }
+
+ if (!ASN1Type.SEQUENCE.equals(sequence.getObjType())) {
+ throw new IOException("Invalid DER: not a sequence: " + sequence.getObjType());
+ }
+
+ try (DERParser parser = sequence.createParser()) {
+ // Skip version
+ ASN1Object versionObject = parser.readObject();
+ if (versionObject == null) {
+ throw new StreamCorruptedException("No version");
+ }
+
+ // as per RFC-3447 section A.1.2
+ BigInteger version = versionObject.asInteger();
+ if (!BigInteger.ZERO.equals(version)) {
+ throw new StreamCorruptedException("Multi-primes N/A");
+ }
+
+ BigInteger modulus = parser.readObject().asInteger();
+ BigInteger publicExp = parser.readObject().asInteger();
+ PublicKey pubKey = kf.generatePublic(new RSAPublicKeySpec(modulus, publicExp));
+
+ BigInteger privateExp = parser.readObject().asInteger();
+ BigInteger primeP = parser.readObject().asInteger();
+ BigInteger primeQ = parser.readObject().asInteger();
+ BigInteger primeExponentP = parser.readObject().asInteger();
+ BigInteger primeExponentQ = parser.readObject().asInteger();
+ BigInteger crtCoef = parser.readObject().asInteger();
+ RSAPrivateKeySpec prvSpec = new RSAPrivateCrtKeySpec(
+ modulus, publicExp, privateExp, primeP, primeQ, primeExponentP, primeExponentQ, crtCoef);
+ PrivateKey prvKey = kf.generatePrivate(prvSpec);
+ return new KeyPair(pubKey, prvKey);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/digest/BaseDigest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/digest/BaseDigest.java b/sshd-common/src/main/java/org/apache/sshd/common/digest/BaseDigest.java
new file mode 100644
index 0000000..a5ef6f9
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/digest/BaseDigest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.digest;
+
+import java.security.MessageDigest;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Base class for Digest algorithms based on the JCE provider.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BaseDigest implements Digest {
+
+ private final String algorithm;
+ private final int bsize;
+ private int h;
+ private String s;
+ private MessageDigest md;
+
+ /**
+ * Create a new digest using the given algorithm and block size.
+ * The initialization and creation of the underlying {@link MessageDigest}
+ * object will be done in the {@link #init()} method.
+ *
+ * @param algorithm the JCE algorithm to use for this digest
+ * @param bsize the block size of this digest
+ */
+ public BaseDigest(String algorithm, int bsize) {
+ this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm");
+ ValidateUtils.checkTrue(bsize > 0, "Invalid block size: %d", bsize);
+ this.bsize = bsize;
+ }
+
+ @Override
+ public final String getAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public int getBlockSize() {
+ return bsize;
+ }
+
+ @Override
+ public void init() throws Exception {
+ this.md = SecurityUtils.getMessageDigest(getAlgorithm());
+ }
+
+ @Override
+ public void update(byte[] data) throws Exception {
+ update(data, 0, NumberUtils.length(data));
+ }
+
+ @Override
+ public void update(byte[] data, int start, int len) throws Exception {
+ Objects.requireNonNull(md, "Digest not initialized").update(data, start, len);
+ }
+
+ /**
+ * @return The current {@link MessageDigest} - may be {@code null} if {@link #init()} not called
+ */
+ protected MessageDigest getMessageDigest() {
+ return md;
+ }
+
+ @Override
+ public byte[] digest() throws Exception {
+ return Objects.requireNonNull(md, "Digest not initialized").digest();
+ }
+
+ @Override
+ public int hashCode() {
+ synchronized (this) {
+ if (h == 0) {
+ h = Objects.hashCode(getAlgorithm()) + getBlockSize();
+ if (h == 0) {
+ h = 1;
+ }
+ }
+ }
+
+ return h;
+ }
+
+ @Override
+ public int compareTo(Digest that) {
+ if (that == null) {
+ return -1; // push null(s) to end
+ } else if (this == that) {
+ return 0;
+ }
+
+ String thisAlg = getAlgorithm();
+ String thatAlg = that.getAlgorithm();
+ int nRes = GenericUtils.safeCompare(thisAlg, thatAlg, false);
+ if (nRes != 0) {
+ return nRes; // debug breakpoint
+ }
+
+ nRes = Integer.compare(this.getBlockSize(), that.getBlockSize());
+ if (nRes != 0) {
+ return nRes; // debug breakpoint
+ }
+
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (obj == this) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ int nRes = compareTo((Digest) obj);
+ return nRes == 0;
+ }
+
+ @Override
+ public String toString() {
+ synchronized (this) {
+ if (s == null) {
+ s = getClass().getSimpleName() + "[" + getAlgorithm() + ":" + getBlockSize() + "]";
+ }
+ }
+
+ return s;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java b/sshd-common/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
new file mode 100644
index 0000000..f469583
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
@@ -0,0 +1,166 @@
+/*
+ * 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.digest;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Provides easy access to the currently implemented digests
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum BuiltinDigests implements DigestFactory {
+ md5(Constants.MD5, "MD5", 16),
+ sha1(Constants.SHA1, "SHA-1", 20),
+ sha224(Constants.SHA224, "SHA-224", 28),
+ sha256(Constants.SHA256, "SHA-256", 32),
+ sha384(Constants.SHA384, "SHA-384", 48),
+ sha512(Constants.SHA512, "SHA-512", 64);
+
+ public static final Set<BuiltinDigests> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(BuiltinDigests.class));
+
+ private final String algorithm;
+ private final int blockSize;
+ private final String factoryName;
+ private final boolean supported;
+
+ BuiltinDigests(String factoryName, String algorithm, int blockSize) {
+ this.factoryName = factoryName;
+ this.algorithm = algorithm;
+ this.blockSize = blockSize;
+ /*
+ * 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 = DigestUtils.checkSupported(algorithm);
+ }
+
+ @Override
+ public final String getName() {
+ return factoryName;
+ }
+
+ @Override
+ public final String getAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public final int getBlockSize() {
+ return blockSize;
+ }
+
+ @Override
+ public final String toString() {
+ return getName();
+ }
+
+ @Override
+ public final Digest create() {
+ return new BaseDigest(getAlgorithm(), getBlockSize());
+ }
+
+ @Override
+ public final boolean isSupported() {
+ return supported;
+ }
+
+ /**
+ * @param s The {@link Enum}'s name - ignored if {@code null}/empty
+ * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose {@link Enum#name()} matches
+ * (case <U>insensitive</U>) the provided argument - {@code null} if no match
+ */
+ public static BuiltinDigests fromString(String s) {
+ if (GenericUtils.isEmpty(s)) {
+ return null;
+ }
+
+ for (BuiltinDigests c : VALUES) {
+ if (s.equalsIgnoreCase(c.name())) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param factory The {@link org.apache.sshd.common.NamedFactory} for the cipher - ignored if {@code null}
+ * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose factory name matches
+ * (case <U>insensitive</U>) the digest factory name
+ * @see #fromFactoryName(String)
+ */
+ public static BuiltinDigests fromFactory(NamedFactory<? extends Digest> 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 org.apache.sshd.common.digest.BuiltinDigests} whose factory name matches
+ * (case <U>insensitive</U>) the provided name - {@code null} if no match
+ */
+ public static BuiltinDigests fromFactoryName(String name) {
+ return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+ }
+
+ /**
+ * @param d The {@link Digest} instance - ignored if {@code null}
+ * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose algorithm matches
+ * (case <U>insensitive</U>) the digets's algorithm - {@code null} if no match
+ */
+ public static BuiltinDigests fromDigest(Digest d) {
+ return fromAlgorithm((d == null) ? null : d.getAlgorithm());
+ }
+
+ /**
+ * @param algo The algorithm to find - ignored if {@code null}/empty
+ * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose algorithm matches
+ * (case <U>insensitive</U>) the provided name - {@code null} if no match
+ */
+ public static BuiltinDigests fromAlgorithm(String algo) {
+ return DigestUtils.findFactoryByAlgorithm(algo, String.CASE_INSENSITIVE_ORDER, VALUES);
+ }
+
+ public static final class Constants {
+ public static final String MD5 = "md5";
+ public static final String SHA1 = "sha1";
+ public static final String SHA224 = "sha224";
+ public static final String SHA256 = "sha256";
+ public static final String SHA384 = "sha384";
+ public static final String SHA512 = "sha512";
+
+ private Constants() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/digest/Digest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/digest/Digest.java b/sshd-common/src/main/java/org/apache/sshd/common/digest/Digest.java
new file mode 100644
index 0000000..27204ff
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/digest/Digest.java
@@ -0,0 +1,36 @@
+/*
+ * 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.digest;
+
+/**
+ * Interface used to compute digests, based on algorithms such as MD5 or SHA1.
+ * The digest implementation are compared first by the algorithm name (case
+ * <U>insensitive</U> and second according to the block size
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Digest extends DigestInformation, Comparable<Digest> {
+ void init() throws Exception;
+
+ void update(byte[] data) throws Exception;
+
+ void update(byte[] data, int start, int len) throws Exception;
+
+ byte[] digest() throws Exception;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestFactory.java
new file mode 100644
index 0000000..f00e7ba
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestFactory.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.digest;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+// CHECKSTYLE:OFF
+public interface DigestFactory extends DigestInformation, NamedFactory<Digest>, OptionalFeature {
+ // 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/digest/DigestInformation.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestInformation.java b/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestInformation.java
new file mode 100644
index 0000000..ba181dc
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestInformation.java
@@ -0,0 +1,36 @@
+/*
+ * 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.digest;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface DigestInformation {
+ /**
+ * @return The digest algorithm name
+ */
+ String getAlgorithm();
+
+ /**
+ * @return The number of bytes in the digest's output
+ */
+ int getBlockSize();
+
+}
[32/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java
new file mode 100644
index 0000000..48f7ec4
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class KeyUtilsCloneTest extends JUnitTestSupport {
+ private final String keyType;
+ private final int keySize;
+
+ public KeyUtilsCloneTest(String keyType, int keySize) {
+ this.keyType = ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type specified");
+ this.keySize = keySize;
+ }
+
+ @Parameters(name = "type={0}, size={1}")
+ public static List<Object[]> parameters() {
+ List<Object[]> list = new ArrayList<>();
+ addTests(list, KeyPairProvider.SSH_DSS, DSS_SIZES);
+ addTests(list, KeyPairProvider.SSH_RSA, RSA_SIZES);
+ if (SecurityUtils.isECCSupported()) {
+ for (ECCurves curve : ECCurves.VALUES) {
+ if (!curve.isSupported()) {
+ continue;
+ }
+ addTests(list, curve.getKeyType(), Collections.singletonList(curve.getKeySize()));
+ }
+ }
+ if (SecurityUtils.isEDDSACurveSupported()) {
+ addTests(list, KeyPairProvider.SSH_ED25519, ED25519_SIZES);
+ }
+ return Collections.unmodifiableList(list);
+ }
+
+ private static void addTests(List<Object[]> list, String keyType, Collection<Integer> sizes) {
+ for (Integer keySize : sizes) {
+ list.add(new Object[]{keyType, keySize});
+ }
+ }
+
+ @Test
+ @SuppressWarnings("checkstyle:avoidnestedblocks")
+ public void testKeyPairCloning() throws GeneralSecurityException {
+ outputDebugMessage("generateKeyPair(%s)[%d]", keyType, keySize);
+ KeyPair original = KeyUtils.generateKeyPair(keyType, keySize);
+
+ outputDebugMessage("cloneKeyPair(%s)[%d]", keyType, keySize);
+ KeyPair cloned = KeyUtils.cloneKeyPair(keyType, original);
+
+ String prefix = keyType + "[" + keySize + "]";
+ assertNotSame(prefix + ": Key pair not cloned", original, cloned);
+ assertTrue(prefix + ": Cloned pair not equals", KeyUtils.compareKeyPairs(original, cloned));
+
+ {
+ PublicKey k1 = original.getPublic();
+ PublicKey k2 = cloned.getPublic();
+ assertNotSame(prefix + ": Public key not cloned", k1, k2);
+ assertTrue(prefix + ": Cloned public key not equals", KeyUtils.compareKeys(k1, k2));
+
+ String f1 = KeyUtils.getFingerPrint(k1);
+ String f2 = KeyUtils.getFingerPrint(k2);
+ assertEquals(prefix + ": Mismatched fingerprints", f1, f2);
+ }
+
+ {
+ PrivateKey k1 = original.getPrivate();
+ PrivateKey k2 = cloned.getPrivate();
+ assertNotSame(prefix + ": Private key not cloned", k1, k2);
+ assertTrue(prefix + ": Cloned private key not equals", KeyUtils.compareKeys(k1, k2));
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintCaseSensitivityTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintCaseSensitivityTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintCaseSensitivityTest.java
new file mode 100644
index 0000000..ae0b86d
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintCaseSensitivityTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class KeyUtilsFingerprintCaseSensitivityTest extends JUnitTestSupport {
+
+ // CHECKSTYLE:OFF
+ private static final String KEY_STRING = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2ctGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZBgIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlEaaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwhV+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMw== lgoldstein@LGOLDSTEIN-WIN7";
+ // CHECKSTYLE:ON
+ private static final String MD5_PREFIX = "MD5:";
+ private static final String MD5 = "24:32:3c:80:01:b3:e1:fa:7c:53:ca:e3:e8:4e:c6:8e";
+ private static final String MD5_FULL = MD5_PREFIX + MD5;
+ private static final String SHA1_PREFIX = "SHA1:";
+ private static final String SHA1 = "ZNLzC6u+F37oq8BpEAwP69EQtoA";
+ private static final String SHA1_FULL = SHA1_PREFIX + SHA1;
+
+ private static PublicKey key;
+
+ private String expected;
+ private String test;
+
+ public KeyUtilsFingerprintCaseSensitivityTest(String expected, String test) {
+ this.expected = expected;
+ this.test = test;
+ }
+
+ @BeforeClass
+ public static void beforeClass() throws GeneralSecurityException, IOException {
+ key = PublicKeyEntry.parsePublicKeyEntry(KEY_STRING).resolvePublicKey(PublicKeyEntryResolver.FAILING);
+ }
+
+ @Parameters(name = "expected={0}, test={1}")
+ public static Collection<Object[]> parameters() {
+ return Arrays.asList(
+ new Object[] {MD5_FULL, MD5_FULL},
+ new Object[] {MD5_FULL, MD5_FULL.toUpperCase()},
+ new Object[] {MD5_FULL, MD5_FULL.toLowerCase()},
+ new Object[] {MD5_FULL, MD5_PREFIX.toUpperCase() + MD5},
+ new Object[] {MD5_FULL, MD5_PREFIX.toLowerCase() + MD5},
+ new Object[] {MD5_FULL, MD5.toLowerCase()},
+ new Object[] {MD5_FULL, MD5.toUpperCase()},
+ new Object[] {SHA1_FULL, SHA1_FULL},
+ new Object[] {SHA1_FULL, SHA1_PREFIX.toUpperCase() + SHA1},
+ new Object[] {SHA1_FULL, SHA1_PREFIX.toLowerCase() + SHA1}
+ );
+ }
+
+ @Test
+ public void testCase() throws Exception {
+ assertEquals("Check failed", new SimpleImmutableEntry<>(true, expected), KeyUtils.checkFingerPrint(test, key));
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintGenerationTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintGenerationTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintGenerationTest.java
new file mode 100644
index 0000000..9a8fb7a
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintGenerationTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sshd.common.digest.BuiltinDigests;
+import org.apache.sshd.common.digest.DigestFactory;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class KeyUtilsFingerprintGenerationTest extends JUnitTestSupport {
+ private final PublicKey key;
+ private final DigestFactory digestFactory;
+ private final String expected;
+
+ public KeyUtilsFingerprintGenerationTest(PublicKey key, DigestFactory digestFactory, String expected) {
+ this.key = key;
+ this.digestFactory = digestFactory;
+ this.expected = expected;
+ }
+
+ @Parameters(name = "key={0}, digestFactory={1}, expected={2}")
+ public static Collection<Object[]> parameters() throws IOException, GeneralSecurityException {
+ List<? extends Map.Entry<String, List<? extends Map.Entry<DigestFactory, String>>>> keyEntries =
+ Collections.unmodifiableList(Arrays.asList(
+ new SimpleImmutableEntry<>(
+ // CHECKSTYLE:OFF
+ "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2ctGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZBgIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlEaaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwhV+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMw== lgoldstein@LGOLDSTEIN-WIN7",
+ // CHECKSTYLE:ON
+ Arrays.asList(
+ new SimpleImmutableEntry<>(
+ BuiltinDigests.md5,
+ "MD5:24:32:3c:80:01:b3:e1:fa:7c:53:ca:e3:e8:4e:c6:8e"
+ ),
+ new SimpleImmutableEntry<>(
+ BuiltinDigests.sha256,
+ "SHA256:1wNOZO+/XgNGJMx8UUJst33V+bBMTz5EcL0B6y2iRv0"
+ )
+ )
+ ),
+ new SimpleImmutableEntry<>(
+ // CHECKSTYLE:OFF
+ "ssh-dss AAAAB3NzaC1kc3MAAACBAMg/IxsG5BxnF5gM7IKqqR0rftxZC+n5GlbO+J4H+iIb/KR8NBehkxG3CrBZMF96M2K1sEGYLob+3k4r71oWaPul8n5rt9kpd+JSq4iD2ygOyg6Kd1/YDBHoxneizy6I/bGsLwhAAKWcRNrXmYVKGzhrhvZWN12AJDq2mGdj3szLAAAAFQD7a2MltdUSF7FU3//SpW4WGjZbeQAAAIBf0nNsfKQL/TEMo7IpTrEMg5V0RnSigCX0+yUERS42GW/ZeCZBJw7oL2XZbuBtu63vMjDgVpnb92BdrcPgjJ7EFW6DlcyeuywStmg1ygXmDR2AQCxv0eX2CQgrdUczmRa155SDVUTvTQlO1IyKx0vwKAh1H7E3yJUfkTAJstbGYQAAAIEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+abYpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLaXWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7A= dsa-key-20130709",
+ // CHECKSTYLE:ON
+ Arrays.asList(
+ new SimpleImmutableEntry<>(
+ BuiltinDigests.md5,
+ "MD5:fb:29:14:8d:94:f9:1d:cf:6b:0e:a4:35:1d:83:44:2f"
+ ),
+ new SimpleImmutableEntry<>(
+ BuiltinDigests.sha256,
+ "SHA256:grxw4KhY1cK6eOczBWs7tDVvo9V0PQw4E1wN1gJvHlw"
+ )
+ )
+ ),
+ new SimpleImmutableEntry<>(
+ // CHECKSTYLE:OFF
+ "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBFImZtcTj842stlcVHLFBFxTEx7lu3jW9aZCvd0r9fUNKZ6LbRPh6l1oJ4ozArnw7XreQBUc5oNd9HB5RNJ8jl1nWXY5cXBA7McZrKZrYmk+zxNhH6UL+kMLaJkyngJHQw== root@osv-linux",
+ // CHECKSTYLE:ON
+ Arrays.asList(
+ new SimpleImmutableEntry<>(
+ BuiltinDigests.md5,
+ "MD5:e6:dc:a2:4f:5b:11:b2:3c:0f:e8:f6:d8:d1:01:e9:d3"
+ ),
+ new SimpleImmutableEntry<>(
+ BuiltinDigests.sha512,
+ "SHA512:4w6ZB78tmFWhpN2J50Ok6WeMJhZp1X0xN0EKWxZmRLcYDbCWhyJDe8lgrQKWqdTCMZ5aNEBl9xQUklcC5Gt2jg"
+ )
+ )
+ )
+ ));
+
+ List<Object[]> ret = new ArrayList<>();
+ for (Map.Entry<String, ? extends Collection<? extends Map.Entry<DigestFactory, String>>> kentry : keyEntries) {
+ String keyValue = kentry.getKey();
+ try {
+ PublicKey key = PublicKeyEntry.parsePublicKeyEntry(keyValue).resolvePublicKey(PublicKeyEntryResolver.FAILING);
+ for (Map.Entry<DigestFactory, String> dentry : kentry.getValue()) {
+ DigestFactory factory = dentry.getKey();
+ String fingerprint = dentry.getValue();
+ if (!factory.isSupported()) {
+ System.out.println("Skip unsupported digest: " + fingerprint);
+ continue;
+ }
+
+ ret.add(new Object[]{key, factory, fingerprint});
+ }
+ } catch (InvalidKeySpecException e) {
+ System.out.println("Skip unsupported key: " + keyValue);
+ }
+ }
+
+ return ret;
+ }
+
+ @Test
+ public void testFingerprint() throws Exception {
+ String name = digestFactory.getName();
+ assertEquals(
+ String.format("Fingerprint does not match for digest %s", name),
+ expected,
+ KeyUtils.getFingerPrint(digestFactory, key)
+ );
+ assertEquals(
+ String.format("Fingerprint check failed for digest %s", name),
+ new SimpleImmutableEntry<>(true, expected),
+ KeyUtils.checkFingerPrint(expected, digestFactory, key)
+ );
+ assertEquals(
+ String.format("Fingerprint check succeeded for invalid digest %s", name),
+ new SimpleImmutableEntry<>(false, expected),
+ KeyUtils.checkFingerPrint(expected + "A", digestFactory, key)
+ );
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java
new file mode 100644
index 0000000..00b273f
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermission;
+import java.security.DigestException;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+
+import org.apache.sshd.common.digest.BaseDigest;
+import org.apache.sshd.common.digest.BuiltinDigests;
+import org.apache.sshd.common.digest.Digest;
+import org.apache.sshd.common.digest.DigestFactory;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.OsUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class KeyUtilsTest extends JUnitTestSupport {
+ public KeyUtilsTest() {
+ super();
+ }
+
+ @Test
+ public void testGenerateFingerPrintOnException() {
+ for (DigestFactory info : BuiltinDigests.VALUES) {
+ if (!info.isSupported()) {
+ System.out.println("Skip unsupported digest: " + info.getAlgorithm());
+ continue;
+ }
+
+ Exception thrown = new DigestException(info.getAlgorithm() + ":" + info.getBlockSize());
+ Digest digest = new BaseDigest(info.getAlgorithm(), info.getBlockSize()) {
+ @Override
+ public byte[] digest() throws Exception {
+ throw thrown;
+ }
+ };
+ String actual = KeyUtils.getFingerPrint(new DigestFactory() {
+ @Override
+ public String getName() {
+ return getCurrentTestName();
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return digest.getAlgorithm();
+ }
+
+ @Override
+ public boolean isSupported() {
+ return info.isSupported();
+ }
+
+ @Override
+ public int getBlockSize() {
+ return info.getBlockSize();
+ }
+
+ @Override
+ public Digest create() {
+ return digest;
+ }
+ }, getCurrentTestName());
+ String expected = thrown.getClass().getSimpleName();
+ assertEquals("Mismatched fingerprint for " + thrown.getMessage(), expected, actual);
+ }
+ }
+
+ @Test
+ public void testGenerateDefaultFingerprintDigest() {
+ DigestFactory defaultValue = KeyUtils.getDefaultFingerPrintFactory();
+ assertNotNull("No current default fingerprint digest factory", defaultValue);
+ try {
+ for (DigestFactory f : BuiltinDigests.VALUES) {
+ if (!f.isSupported()) {
+ System.out.println("Skip unsupported digest=" + f.getAlgorithm());
+ continue;
+ }
+
+ KeyUtils.setDefaultFingerPrintFactory(f);
+
+ String data = getClass().getName() + "#" + getCurrentTestName() + "(" + f.getName() + ")";
+ String expected = KeyUtils.getFingerPrint(f, data);
+ String actual = KeyUtils.getFingerPrint(data);
+ assertEquals("Mismatched fingerprint for digest=" + f.getName(), expected, actual);
+ }
+ } finally {
+ KeyUtils.setDefaultFingerPrintFactory(defaultValue); // restore the original
+ }
+ }
+
+ @Test // see SSHD-606
+ public void testValidateStrictKeyFilePermissions() throws IOException {
+ Path file = getTempTargetRelativeFile(getClass().getSimpleName(), getCurrentTestName());
+ outputDebugMessage("%s deletion result=%s", file, Files.deleteIfExists(file));
+ assertNull("Unexpected violation for non-existent file: " + file, KeyUtils.validateStrictKeyFilePermissions(file));
+
+ assertHierarchyTargetFolderExists(file.getParent());
+ try (OutputStream output = Files.newOutputStream(file)) {
+ output.write((getClass().getName() + "#" + getCurrentTestName() + "@" + new Date(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
+ }
+
+ Collection<PosixFilePermission> perms = IoUtils.getPermissions(file);
+ if (GenericUtils.isEmpty(perms)) {
+ assertNull("Unexpected violation for no permissions file: " + file, KeyUtils.validateStrictKeyFilePermissions(file));
+ } else if (OsUtils.isUNIX()) {
+ Map.Entry<String, Object> violation = null;
+ for (PosixFilePermission p : KeyUtils.STRICTLY_PROHIBITED_FILE_PERMISSION) {
+ if (perms.contains(p)) {
+ violation = KeyUtils.validateStrictKeyFilePermissions(file);
+ assertNotNull("Unexpected success for permission=" + p + " of file " + file + " permissions=" + perms, violation);
+ break;
+ }
+ }
+
+ if (violation == null) { // we expect a failure since the parent does not have the necessary permissions
+ assertNotNull("Unexpected UNIX success for file " + file + " permissions=" + perms, KeyUtils.validateStrictKeyFilePermissions(file));
+ }
+ } else {
+ assertNull("Unexpected Windows violation for file " + file + " permissions=" + perms, KeyUtils.validateStrictKeyFilePermissions(file));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/PublicKeyEntryTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/PublicKeyEntryTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/PublicKeyEntryTest.java
new file mode 100644
index 0000000..1fd372f
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/PublicKeyEntryTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.GeneralSecurityException;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class PublicKeyEntryTest extends JUnitTestSupport {
+ public PublicKeyEntryTest() {
+ super();
+ }
+
+ @Test
+ public void testFallbackResolver() throws Exception {
+ PublicKeyEntry entry = // TODO use some
+ PublicKeyEntry.parsePublicKeyEntry(
+ GenericUtils.join(
+ Arrays.asList(getCurrentTestName(), "AAAA", getClass().getSimpleName()), ' '));
+ for (PublicKeyEntryResolver resolver : new PublicKeyEntryResolver[]{
+ null, PublicKeyEntryResolver.FAILING, PublicKeyEntryResolver.IGNORING}) {
+ try {
+ PublicKey key = entry.resolvePublicKey(resolver);
+ assertSame("Mismatched successful resolver", PublicKeyEntryResolver.IGNORING, resolver);
+ assertNull("Unexpected success for resolver=" + resolver + ": " + KeyUtils.getFingerPrint(key), key);
+ } catch (GeneralSecurityException e) {
+ assertObjectInstanceOf("Mismatched thrown exception for resolver=" + resolver, InvalidKeySpecException.class, e);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java
new file mode 100644
index 0000000..48797b8
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.loader;
+
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.util.List;
+import java.util.Random;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class AESPrivateKeyObfuscatorTest extends JUnitTestSupport {
+ private static final Random RANDOMIZER = new Random(System.currentTimeMillis());
+
+ private final int keyLength;
+
+ public AESPrivateKeyObfuscatorTest(int keyLength) {
+ this.keyLength = keyLength;
+ }
+
+ @Parameters(name = "keyLength={0}")
+ public static List<Object[]> parameters() {
+ List<Integer> lengths = AESPrivateKeyObfuscator.getAvailableKeyLengths();
+ assertFalse("No lengths available", GenericUtils.isEmpty(lengths));
+ return parameterize(lengths);
+ }
+
+ @Test
+ public void testAvailableKeyLengthExists() throws GeneralSecurityException {
+ assertEquals("Not a BYTE size multiple", 0, keyLength % Byte.SIZE);
+
+ byte[] iv = new byte[keyLength / Byte.SIZE];
+ synchronized (RANDOMIZER) {
+ RANDOMIZER.nextBytes(iv);
+ }
+
+ Key key = new SecretKeySpec(iv, AESPrivateKeyObfuscator.CIPHER_NAME);
+ Cipher c = SecurityUtils.getCipher(AESPrivateKeyObfuscator.CIPHER_NAME);
+ c.init(Cipher.DECRYPT_MODE, key);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java
new file mode 100644
index 0000000..d5c6aba
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.loader.openssh;
+
+import java.net.URL;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.BuiltinIdentities;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class OpenSSHKeyPairResourceParserTest extends JUnitTestSupport {
+ private static final OpenSSHKeyPairResourceParser PARSER = OpenSSHKeyPairResourceParser.INSTANCE;
+ private final BuiltinIdentities identity;
+
+ public OpenSSHKeyPairResourceParserTest(BuiltinIdentities identity) {
+ this.identity = identity;
+ }
+
+ @Parameters(name = "type={0}")
+ public static List<Object[]> parameters() {
+ return parameterize(BuiltinIdentities.VALUES);
+ }
+
+ @Test
+ public void testLoadKeyPairs() throws Exception {
+ Assume.assumeTrue(identity + " not supported", identity.isSupported());
+
+ String resourceKey = getClass().getSimpleName() + "-" + identity.getName().toUpperCase() + "-" + KeyPair.class.getSimpleName();
+ URL urlKeyPair = getClass().getResource(resourceKey);
+ assertNotNull("Missing key-pair resource: " + resourceKey, urlKeyPair);
+
+ Collection<KeyPair> pairs = PARSER.loadKeyPairs(urlKeyPair, FilePasswordProvider.EMPTY);
+ assertEquals("Mismatched pairs count", 1, GenericUtils.size(pairs));
+
+ URL urlPubKey = getClass().getResource(resourceKey + ".pub");
+ assertNotNull("Missing public key resource: " + resourceKey, urlPubKey);
+
+ List<AuthorizedKeyEntry> entries = AuthorizedKeyEntry.readAuthorizedKeys(urlPubKey);
+ assertEquals("Mismatched public keys count", 1, GenericUtils.size(entries));
+
+ AuthorizedKeyEntry entry = entries.get(0);
+ PublicKey pubEntry = entry.resolvePublicKey(PublicKeyEntryResolver.FAILING);
+ assertNotNull("Cannot retrieve public key", pubEntry);
+
+ Class<? extends PublicKey> pubType = identity.getPublicKeyType();
+ Class<? extends PrivateKey> prvType = identity.getPrivateKeyType();
+ for (KeyPair kp : pairs) {
+ PublicKey pubKey = ValidateUtils.checkInstanceOf(kp.getPublic(), pubType, "Mismatched public key type");
+ assertKeyEquals("Mismatched identity public key", pubEntry, pubKey);
+
+ PrivateKey prvKey = ValidateUtils.checkInstanceOf(kp.getPrivate(), prvType, "Mismatched private key type");
+ @SuppressWarnings("rawtypes")
+ PrivateKeyEntryDecoder decoder =
+ OpenSSHKeyPairResourceParser.getPrivateKeyEntryDecoder(prvKey);
+ assertNotNull("No private key decoder", decoder);
+
+ if (decoder.isPublicKeyRecoverySupported()) {
+ @SuppressWarnings("unchecked")
+ PublicKey recKey = decoder.recoverPublicKey(prvKey);
+ assertKeyEquals("Mismatched recovered public key", pubKey, recKey);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
new file mode 100644
index 0000000..406a273
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.loader.pem;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.ssl.PEMItem;
+import org.apache.commons.ssl.PEMUtil;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class PKCS8PEMResourceKeyPairParserTest extends JUnitTestSupport {
+ private final String algorithm;
+ private final int keySize;
+
+ public PKCS8PEMResourceKeyPairParserTest(String algorithm, int keySize) {
+ this.algorithm = algorithm;
+ this.keySize = keySize;
+ }
+
+ @Parameters(name = "{0} / {1}")
+ public static List<Object[]> parameters() {
+ List<Object[]> params = new ArrayList<>();
+ for (Integer ks : RSA_SIZES) {
+ params.add(new Object[]{KeyUtils.RSA_ALGORITHM, ks});
+ }
+ for (Integer ks : DSS_SIZES) {
+ params.add(new Object[]{KeyUtils.DSS_ALGORITHM, ks});
+ }
+ return params;
+ }
+
+ @Test // see SSHD-760
+ public void testPkcs8() throws IOException, GeneralSecurityException {
+ KeyPairGenerator generator = SecurityUtils.getKeyPairGenerator(algorithm);
+ if (keySize > 0) {
+ generator.initialize(keySize);
+ }
+ KeyPair kp = generator.generateKeyPair();
+
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ Collection<Object> items = new ArrayList<>();
+ PrivateKey prv1 = kp.getPrivate();
+ items.add(new PEMItem(prv1.getEncoded(), "PRIVATE KEY"));
+ byte[] bytes = PEMUtil.encode(items);
+ os.write(bytes);
+ os.close();
+
+ try (ByteArrayInputStream bais = new ByteArrayInputStream(os.toByteArray())) {
+ KeyPair kp2 = SecurityUtils.loadKeyPairIdentity(getCurrentTestName(), bais, null);
+
+ assertEquals("Mismatched public key", kp.getPublic(), kp2.getPublic());
+ assertEquals("Mismatched private key", prv1, kp2.getPrivate());
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" + algorithm + "/" + keySize + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/digest/BuiltinDigestsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/digest/BuiltinDigestsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/digest/BuiltinDigestsTest.java
new file mode 100644
index 0000000..9ec928b
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/digest/BuiltinDigestsTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.digest;
+
+import java.lang.reflect.Field;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BuiltinDigestsTest extends JUnitTestSupport {
+ public BuiltinDigestsTest() {
+ super();
+ }
+
+ @Test
+ public void testFromName() {
+ for (BuiltinDigests expected : BuiltinDigests.VALUES) {
+ String name = expected.getName();
+ BuiltinDigests actual = BuiltinDigests.fromFactoryName(name);
+ assertSame(name, expected, actual);
+ }
+ }
+
+ @Test
+ public void testAllConstantsCovered() throws Exception {
+ Set<BuiltinDigests> avail = EnumSet.noneOf(BuiltinDigests.class);
+ Field[] fields = BuiltinDigests.Constants.class.getFields();
+ for (Field f : fields) {
+ String name = (String) f.get(null);
+ BuiltinDigests value = BuiltinDigests.fromFactoryName(name);
+ assertNotNull("No match found for " + name, value);
+ assertTrue(name + " re-specified", avail.add(value));
+ }
+
+ assertEquals("Incomplete coverage", BuiltinDigests.VALUES, avail);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/future/DefaultSshFutureTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/future/DefaultSshFutureTest.java b/sshd-common/src/test/java/org/apache/sshd/common/future/DefaultSshFutureTest.java
new file mode 100644
index 0000000..05e3df2
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/future/DefaultSshFutureTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.future;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+import org.mockito.Mockito;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class DefaultSshFutureTest extends JUnitTestSupport {
+ public DefaultSshFutureTest() {
+ super();
+ }
+
+ @Test
+ @SuppressWarnings("rawtypes")
+ public void testAwaitUninterrupted() {
+ DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
+ Object expected = new Object();
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(TimeUnit.SECONDS.toMillis(2L));
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ future.setValue(expected);
+ }
+ }.start();
+
+ future.awaitUninterruptibly();
+ assertSame("Mismatched signalled value", expected, future.getValue());
+ }
+
+ @Test
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public void testNotifyMultipleListeners() {
+ DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
+ AtomicInteger listenerCount = new AtomicInteger(0);
+ Object expected = new Object();
+ SshFutureListener listener = f -> {
+ assertSame("Mismatched future instance", future, f);
+ assertSame("Mismatched value object", expected, future.getValue());
+ listenerCount.incrementAndGet();
+ };
+
+ int numListeners = Byte.SIZE;
+ for (int index = 0; index < numListeners; index++) {
+ future.addListener(listener);
+ }
+
+ future.setValue(expected);
+ assertEquals("Mismatched listeners invocation count", numListeners, listenerCount.get());
+ }
+
+ @Test
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public void testListenerInvokedDirectlyAfterResultSet() {
+ DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
+ AtomicInteger listenerCount = new AtomicInteger(0);
+ Object expected = new Object();
+ SshFutureListener listener = f -> {
+ assertSame("Mismatched future instance", future, f);
+ assertSame("Mismatched value object", expected, future.getValue());
+ listenerCount.incrementAndGet();
+ };
+ future.setValue(expected);
+
+ future.addListener(listener);
+ assertEquals("Mismatched number of registered listeners", 0, future.getNumRegisteredListeners());
+ assertEquals("Listener not invoked", 1, listenerCount.get());
+ }
+
+ @Test
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public void testAddAndRemoveRegisteredListenersBeforeResultSet() {
+ DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
+ SshFutureListener listener = Mockito.mock(SshFutureListener.class);
+ for (int index = 1; index <= Byte.SIZE; index++) {
+ future.addListener(listener);
+ assertEquals("Mismatched number of added listeners", index, future.getNumRegisteredListeners());
+ }
+
+ for (int index = future.getNumRegisteredListeners() - 1; index >= 0; index--) {
+ future.removeListener(listener);
+ assertEquals("Mismatched number of remaining listeners", index, future.getNumRegisteredListeners());
+ }
+ }
+
+ @Test
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public void testListenerNotRemovedIfResultSet() {
+ DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
+ AtomicInteger listenerCount = new AtomicInteger(0);
+ Object expected = new Object();
+ SshFutureListener listener = f -> {
+ assertSame("Mismatched future instance", future, f);
+ assertSame("Mismatched value object", expected, future.getValue());
+ listenerCount.incrementAndGet();
+ };
+ future.addListener(listener);
+ future.setValue(expected);
+ assertEquals("Mismatched number of registered listeners", 1, future.getNumRegisteredListeners());
+ assertEquals("Listener not invoked", 1, listenerCount.get());
+
+ future.removeListener(listener);
+ assertEquals("Mismatched number of remaining listeners", 1, future.getNumRegisteredListeners());
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
new file mode 100644
index 0000000..021796b
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.keyprovider;
+
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.function.Function;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+import org.mockito.Mockito;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class KeyPairProviderTest extends JUnitTestSupport {
+ public KeyPairProviderTest() {
+ super();
+ }
+
+ @Test
+ public void testEmptyKeyProvider() {
+ KeyPairProvider provider = KeyPairProvider.EMPTY_KEYPAIR_PROVIDER;
+ assertTrue("Non empty loaded keys", GenericUtils.isEmpty(provider.loadKeys()));
+ assertTrue("Non empty key type", GenericUtils.isEmpty(provider.getKeyTypes()));
+
+ for (String keyType : new String[]{null, "", getCurrentTestName()}) {
+ assertNull("Unexpected key-pair loaded for type='" + keyType + "'", provider.loadKey(keyType));
+ }
+ }
+
+ @Test
+ public void testMapToKeyPairProvider() {
+ PublicKey pubKey = Mockito.mock(PublicKey.class);
+ PrivateKey prvKey = Mockito.mock(PrivateKey.class);
+ String[] testKeys = {getCurrentTestName(), getClass().getSimpleName()};
+ Map<String, KeyPair> pairsMap = GenericUtils.toSortedMap(
+ Arrays.asList(testKeys),
+ Function.identity(),
+ k -> new KeyPair(pubKey, prvKey),
+ String.CASE_INSENSITIVE_ORDER);
+
+ KeyPairProvider provider = MappedKeyPairProvider.MAP_TO_KEY_PAIR_PROVIDER.apply(pairsMap);
+ assertEquals("Key types", pairsMap.keySet(), provider.getKeyTypes());
+ assertEquals("Key pairs", pairsMap.values(), provider.loadKeys());
+
+ pairsMap.forEach((keyType, expected) -> {
+ KeyPair actual = provider.loadKey(keyType);
+ assertSame(keyType, expected, actual);
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
new file mode 100644
index 0000000..0d50c41
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.mac;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.mac.BuiltinMacs.ParseResult;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+import org.mockito.Mockito;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BuiltinMacsTest extends JUnitTestSupport {
+ public BuiltinMacsTest() {
+ super();
+ }
+
+ @Test
+ public void testFromName() {
+ for (BuiltinMacs expected : BuiltinMacs.VALUES) {
+ String name = expected.getName();
+ BuiltinMacs actual = BuiltinMacs.fromFactoryName(name);
+ assertSame(name, expected, actual);
+ }
+ }
+
+ @Test
+ public void testAllConstantsCovered() throws Exception {
+ Set<BuiltinMacs> avail = EnumSet.noneOf(BuiltinMacs.class);
+ Field[] fields = BuiltinMacs.Constants.class.getFields();
+ for (Field f : fields) {
+ String name = (String) f.get(null);
+ BuiltinMacs value = BuiltinMacs.fromFactoryName(name);
+ assertNotNull("No match found for " + name, value);
+ assertTrue(name + " re-specified", avail.add(value));
+ }
+
+ assertEquals("Incomplete coverage", BuiltinMacs.VALUES, avail);
+ }
+
+ @Test
+ public void testParseMacsList() {
+ List<String> builtin = NamedResource.getNameList(BuiltinMacs.VALUES);
+ List<String> unknown = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
+ Random rnd = new Random();
+ for (int index = 0; index < (builtin.size() + unknown.size()); index++) {
+ Collections.shuffle(builtin, rnd);
+ Collections.shuffle(unknown, rnd);
+
+ List<String> weavedList = new ArrayList<>(builtin.size() + unknown.size());
+ for (int bIndex = 0, uIndex = 0; (bIndex < builtin.size()) || (uIndex < unknown.size());) {
+ boolean useBuiltin = false;
+ if (bIndex < builtin.size()) {
+ useBuiltin = uIndex >= unknown.size() || rnd.nextBoolean();
+ }
+
+ if (useBuiltin) {
+ weavedList.add(builtin.get(bIndex));
+ bIndex++;
+ } else if (uIndex < unknown.size()) {
+ weavedList.add(unknown.get(uIndex));
+ uIndex++;
+ }
+ }
+
+ String fullList = GenericUtils.join(weavedList, ',');
+ ParseResult result = BuiltinMacs.parseMacsList(fullList);
+ List<String> parsed = NamedResource.getNameList(result.getParsedFactories());
+ List<String> missing = result.getUnsupportedFactories();
+
+ // makes sure not only that the contents are the same but also the order
+ assertListEquals(fullList + "[parsed]", builtin, parsed);
+ assertListEquals(fullList + "[unsupported]", unknown, missing);
+ }
+ }
+
+ @Test
+ public void testResolveFactoryOnBuiltinValues() {
+ for (MacFactory expected : BuiltinMacs.VALUES) {
+ String name = expected.getName();
+ MacFactory actual = BuiltinMacs.resolveFactory(name);
+ assertSame(name, expected, actual);
+ }
+ }
+
+ @Test
+ public void testNotAllowedToRegisterBuiltinFactories() {
+ for (MacFactory expected : BuiltinMacs.VALUES) {
+ try {
+ BuiltinMacs.registerExtension(expected);
+ fail("Unexpected success for " + expected.getName());
+ } catch (IllegalArgumentException e) {
+ // expected - ignored
+ }
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNotAllowedToOverrideRegisteredFactories() {
+ MacFactory expected = Mockito.mock(MacFactory.class);
+ Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+ String name = expected.getName();
+ try {
+ for (int index = 1; index <= Byte.SIZE; index++) {
+ BuiltinMacs.registerExtension(expected);
+ assertEquals("Unexpected success at attempt #" + index, 1, index);
+ }
+ } finally {
+ BuiltinMacs.unregisterExtension(name);
+ }
+ }
+
+ @Test
+ public void testResolveFactoryOnRegisteredExtension() {
+ MacFactory expected = Mockito.mock(MacFactory.class);
+ Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+ String name = expected.getName();
+ try {
+ assertNull("Extension already registered", BuiltinMacs.resolveFactory(name));
+ BuiltinMacs.registerExtension(expected);
+
+ MacFactory actual = BuiltinMacs.resolveFactory(name);
+ assertSame("Mismatched resolved instance", expected, actual);
+ } finally {
+ MacFactory actual = BuiltinMacs.unregisterExtension(name);
+ assertSame("Mismatched unregistered instance", expected, actual);
+ assertNull("Extension not un-registered", BuiltinMacs.resolveFactory(name));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java
new file mode 100644
index 0000000..f330d22
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java
@@ -0,0 +1,311 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.mac;
+
+import java.nio.charset.StandardCharsets;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @see <A HREF="https://tools.ietf.org/html/rfc4231">RFC 4321</A>
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class MacVectorsTest extends JUnitTestSupport {
+ private final VectorSeed seed;
+ private final Factory<? extends Mac> macFactory;
+ private final byte[] expected;
+
+ public MacVectorsTest(VectorSeed seed, String factoryName, String expected) {
+ this.seed = Objects.requireNonNull(seed, "No seed");
+ this.macFactory = ValidateUtils.checkNotNull(BuiltinMacs.fromFactoryName(factoryName), "Unknown MAC: %s", factoryName);
+ this.expected = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, expected);
+ }
+
+ @Parameters(name = "factory={1}, expected={2}, seed={0}")
+ public static Collection<Object[]> parameters() {
+ List<Object[]> ret = new ArrayList<>();
+ for (VectorTestData vector : Collections.unmodifiableList(
+ Arrays.asList(
+ ///////////////// Test Cases for HMAC-MD5 ///////////////////////
+ // see https://tools.ietf.org/html/rfc2202
+ new VectorTestData("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", false, "Hi There",
+ Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5, // test case 1
+ "9294727a3638bb1c13f48ef8158bfc9d"))),
+ new VectorTestData("Jefe", "what do ya want for nothing?",
+ Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5, // test case 2
+ "750c783e6ab0b503eaa86e310a5db738"))),
+ new VectorTestData("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", false, repeat("dd", 50), false,
+ Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5, // test case 3
+ "56be34521d144c88dbb8c733f0e8b3f6"))),
+ /* TODO see why it fails
+ new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
+ Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5, // test case 4
+ "697eaf0aca3a3aea3a75164746ffaa79"))),
+ */
+ new VectorTestData("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", false, "Test With Truncation",
+ Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5, // test case 5
+ "56461ef2342edc00f9bab995690efd4c"),
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5_96,
+ "56461ef2342edc00f9bab995"))),
+ /* TODO see why it fails
+ new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
+ Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5, // test case 6
+ "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd"))),
+ */
+ /* TODO see why it fails
+ new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+ Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5, // test case 7
+ "6f630fad67cda0ee1fb1f562db3aa53e"))),
+ */
+ ///////////////// Test Cases for HMAC-SHA-1 ///////////////////////
+ // see https://tools.ietf.org/html/rfc2202
+ new VectorTestData("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", false, "Hi There",
+ Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 1
+ "b617318655057264e28bc0b6fb378c8ef146be00"))),
+ new VectorTestData("Jefe", "what do ya want for nothing?",
+ Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 2
+ "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"))),
+ new VectorTestData("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", false, repeat("dd", 50), false,
+ Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 3
+ "125d7342b9ac11cd91a39af48aa17b4f63f175d3"))),
+ /* TODO see why it fails
+ new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
+ Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 4
+ "4c9007f4026250c6bc8414f9bf50c86c2d7235da"))),
+ */
+ new VectorTestData("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", false, "Test With Truncation",
+ Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 5
+ "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"),
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1_96,
+ "4c1a03424b55e07fe7f27be1"))),
+ /* TODO see why this fails
+ new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
+ Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 6
+ "aa4ae5e15272d00e95705637ce8a3b55ed402112"))),
+ */
+
+ /* TODO see why it fails
+ new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+ Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 7
+ "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"),
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1_96,
+ "4c1a03424b55e07fe7f27be1"))),
+ */
+
+ /* TODO see why it fails
+ new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
+ Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 8
+ "aa4ae5e15272d00e95705637ce8a3b55ed402112"))),
+ */
+
+ /* TODO see why it fails
+ new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+ Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 9
+ "e8e99d0f45237d786d6bbaa7965c7808bbff1a91"))),
+ */
+
+ ///////////////// Test Cases for HMAC-SHA-2 ///////////////////////
+ // see https://tools.ietf.org/html/rfc4231
+ new VectorTestData(repeat("0b", 20), false, "Hi There",
+ // test case 1
+ Arrays.asList(
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+ "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"),
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+ "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"))),
+ new VectorTestData("Jefe", "what do ya want for nothing?",
+ // test case 2
+ Arrays.asList(
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+ "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"),
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+ "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"))),
+ new VectorTestData(repeat("aa", 20), false, repeat("dd", 50), false,
+ // test case 3
+ Arrays.asList(
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+ "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"),
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+ "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"))),
+ new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
+ // test case 4
+ Arrays.asList(
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+ "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"),
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+ "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd"))),
+
+ /* TODO see why it fails
+ new VectorTestData(repeat("0c", 20), false, "Test With Truncation",
+ Arrays.asList( // test case 5
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+ "a3b6167473100ee06e0c796c2955552b"),
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+ "415fad6271580a531d4179bc891d87a6"))),
+ */
+
+ /* TODO see why it fails
+ new VectorTestData(repeat("aa", 131), false, "Test Using Larger Than Block-Size Key - Hash Key First",
+ Arrays.asList( // test case 6
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+ "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"),
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+ "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598"))),
+ */
+
+ /* TODO see why it fails
+ new VectorTestData(repeat("aa", 131), false, "This is a test using a larger than block-size"
+ + " key and a larger than block-size data."
+ + " The key needs to be hashed before being used"
+ + " by the HMAC algorithm",
+ Arrays.asList( // test case 7
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+ "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"),
+ new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+ "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58")))
+ */
+
+ // mark end
+ new VectorTestData("", false, "", false, Collections.emptyList())))) {
+ for (Map.Entry<String, String> tc : vector.getResults()) {
+ ret.add(new Object[]{vector, tc.getKey(), tc.getValue()});
+ }
+ }
+
+ return ret;
+ }
+
+ @Test
+ public void testStandardVectorMac() throws Exception {
+ Mac mac = macFactory.create();
+ mac.init(seed.getKey());
+ mac.update(seed.getData());
+
+ byte[] actual = new byte[mac.getBlockSize()];
+ mac.doFinal(actual);
+ assertArrayEquals("Mismatched results for actual=" + BufferUtils.toHex(BufferUtils.EMPTY_HEX_SEPARATOR, actual), expected, actual);
+ }
+
+ private static class VectorSeed {
+ private final byte[] key;
+ private final String keyString;
+ private final byte[] data;
+ private final String dataString;
+
+ VectorSeed(String key, String data) {
+ this.key = key.getBytes(StandardCharsets.UTF_8);
+ this.keyString = key;
+ this.data = data.getBytes(StandardCharsets.UTF_8);
+ this.dataString = data;
+ }
+
+ VectorSeed(String key, boolean useKeyString, String data) {
+ this.key = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, key);
+ this.keyString = useKeyString ? new String(this.key, StandardCharsets.UTF_8) : key;
+ this.data = data.getBytes(StandardCharsets.UTF_8);
+ this.dataString = data;
+ }
+
+ VectorSeed(String key, boolean useKeyString, String data, boolean useDataString) {
+ this.key = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, key);
+ this.keyString = useKeyString ? new String(this.key, StandardCharsets.UTF_8) : key;
+ this.data = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, data);
+ this.dataString = useDataString ? new String(this.data, StandardCharsets.UTF_8) : data;
+ }
+
+ public byte[] getKey() {
+ return key.clone(); // clone to avoid inadvertent change
+ }
+
+ public String getKeyString() {
+ return keyString;
+ }
+
+ public byte[] getData() {
+ return data.clone(); // clone to avoid inadvertent change
+ }
+
+ public String getDataString() {
+ return dataString;
+ }
+
+ @Override
+ public String toString() {
+ return "key=" + trimToLength(getKeyString(), 32) + ", data=" + trimToLength(getDataString(), 32);
+ }
+
+ private static CharSequence trimToLength(CharSequence csq, int maxLen) {
+ if (GenericUtils.length(csq) <= maxLen) {
+ return csq;
+ }
+
+ return csq.subSequence(0, maxLen) + "...";
+ }
+ }
+
+ private static class VectorTestData extends VectorSeed {
+ private final Collection<Map.Entry<String, String>> results;
+
+ VectorTestData(String key, String data, Collection<Map.Entry<String, String>> results) {
+ super(key, data);
+ this.results = results;
+ }
+
+ VectorTestData(String key, boolean useKeyString, String data, Collection<Map.Entry<String, String>> results) {
+ super(key, useKeyString, data);
+ this.results = results;
+ }
+
+ VectorTestData(String key, boolean useKeyString, String data, boolean useDataString, Collection<Map.Entry<String, String>> results) {
+ super(key, useKeyString, data, useDataString);
+ this.results = results;
+ }
+
+ public Collection<Map.Entry<String, String>> getResults() {
+ return results;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/random/RandomFactoryTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/random/RandomFactoryTest.java b/sshd-common/src/test/java/org/apache/sshd/common/random/RandomFactoryTest.java
new file mode 100644
index 0000000..08872d9
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/random/RandomFactoryTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.random;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class RandomFactoryTest extends JUnitTestSupport {
+ private final RandomFactory factory;
+
+ public RandomFactoryTest(RandomFactory factory) {
+ this.factory = factory;
+ }
+
+ @Parameters(name = "type={0}")
+ public static Collection<Object[]> parameters() {
+ Collection<RandomFactory> testCases = new LinkedList<>();
+ testCases.add(JceRandomFactory.INSTANCE);
+ if (SecurityUtils.isBouncyCastleRegistered()) {
+ testCases.add(SecurityUtils.getRandomFactory());
+ } else {
+ System.out.println("Skip BouncyCastleRandomFactory - unsupported");
+ }
+
+ return parameterize(testCases);
+ }
+
+ @Test
+ public void testRandomFactory() {
+ Assume.assumeTrue("Skip unsupported factory: " + factory.getName(), factory.isSupported());
+ long t = testRandom(factory.create());
+ System.out.println(factory.getName() + " duration: " + t + " " + TimeUnit.MICROSECONDS);
+ }
+
+ // returns duration in microseconds
+ private static long testRandom(Random random) {
+ byte[] bytes = new byte[32];
+ long l0 = System.nanoTime();
+ for (int i = 0; i < 1000; i++) {
+ random.fill(bytes, 8, 16);
+ }
+ long l1 = System.nanoTime();
+ return TimeUnit.NANOSECONDS.toMicros(l1 - l0);
+ }
+}
[27/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
deleted file mode 100644
index da5f687..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
+++ /dev/null
@@ -1,95 +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.client.config.hosts;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.io.IoUtils;
-
-/**
- * Monitors the {@code ~/.ssh/config} file of the user currently running
- * the client, re-loading it if necessary. It also (optionally) enforces
- * the same permissions regime as {@code OpenSSH}
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DefaultConfigFileHostEntryResolver extends ConfigFileHostEntryResolver {
- /**
- * The default instance that enforces the same permissions regime as {@code OpenSSH}
- */
- public static final DefaultConfigFileHostEntryResolver INSTANCE = new DefaultConfigFileHostEntryResolver(true);
-
- private final boolean strict;
-
- /**
- * @param strict If {@code true} then makes sure that the containing folder
- * has 0700 access and the file 0644. <B>Note:</B> for <I>Windows</I> it
- * does not check these permissions
- * @see #validateStrictConfigFilePermissions(Path, LinkOption...)
- */
- public DefaultConfigFileHostEntryResolver(boolean strict) {
- this(HostConfigEntry.getDefaultHostConfigFile(), strict);
- }
-
- public DefaultConfigFileHostEntryResolver(File file, boolean strict) {
- this(Objects.requireNonNull(file, "No file provided").toPath(), strict, IoUtils.getLinkOptions(true));
- }
-
- public DefaultConfigFileHostEntryResolver(Path path, boolean strict, LinkOption... options) {
- super(path, options);
- this.strict = strict;
- }
-
- /**
- * @return If {@code true} then makes sure that the containing folder
- * has 0700 access and the file 0644. <B>Note:</B> for <I>Windows</I> it
- * does not check these permissions
- * @see #validateStrictConfigFilePermissions(Path, LinkOption...)
- */
- public final boolean isStrict() {
- return strict;
- }
-
- @Override
- protected List<HostConfigEntry> reloadHostConfigEntries(Path path, String host, int port, String username) throws IOException {
- if (isStrict()) {
- if (log.isDebugEnabled()) {
- log.debug("reloadHostConfigEntries({}@{}:{}) check permissions of {}", username, host, port, path);
- }
-
- Map.Entry<String, ?> violation = validateStrictConfigFilePermissions(path);
- if (violation != null) {
- log.warn("reloadHostConfigEntries({}@{}:{}) invalid file={} permissions: {}",
- username, host, port, path, violation.getKey());
- updateReloadAttributes();
- return Collections.emptyList();
- }
- }
-
- return super.reloadHostConfigEntries(path, host, port, username);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
deleted file mode 100644
index f401cee..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
+++ /dev/null
@@ -1,1169 +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.client.config.hosts;
-
-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.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.StreamCorruptedException;
-import java.io.Writer;
-import java.net.InetAddress;
-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.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-
-import org.apache.sshd.common.auth.MutableUserHolder;
-import org.apache.sshd.common.config.SshConfigFileReader;
-import org.apache.sshd.common.config.keys.IdentityUtils;
-import org.apache.sshd.common.config.keys.PublicKeyEntry;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.OsUtils;
-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.NoCloseOutputStream;
-import org.apache.sshd.common.util.io.NoCloseReader;
-
-/**
- * Represents an entry in the client's configuration file as defined by
- * the <A HREF="http://www.gsp.com/cgi-bin/man.cgi?topic=ssh_config">configuration
- * file format</A>
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class HostConfigEntry extends HostPatternsHolder implements MutableUserHolder {
- /**
- * Standard OpenSSH config file name
- */
- public static final String STD_CONFIG_FILENAME = "config";
-
- public static final String HOST_CONFIG_PROP = "Host";
- public static final String HOST_NAME_CONFIG_PROP = "HostName";
- public static final String PORT_CONFIG_PROP = SshConfigFileReader.PORT_CONFIG_PROP;
- public static final String USER_CONFIG_PROP = "User";
- public static final String IDENTITY_FILE_CONFIG_PROP = "IdentityFile";
- /**
- * Use only the identities specified in the host entry (if any)
- */
- public static final String EXCLUSIVE_IDENTITIES_CONFIG_PROP = "IdentitiesOnly";
- public static final boolean DEFAULT_EXCLUSIVE_IDENTITIES = false;
-
- /**
- * A case <U>insensitive</U> {@link Set} of the properties that receive special handling
- */
- public static final Set<String> EXPLICIT_PROPERTIES =
- Collections.unmodifiableSet(
- GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER,
- HOST_CONFIG_PROP, HOST_NAME_CONFIG_PROP, PORT_CONFIG_PROP,
- USER_CONFIG_PROP, IDENTITY_FILE_CONFIG_PROP, EXCLUSIVE_IDENTITIES_CONFIG_PROP
- ));
-
- public static final String MULTI_VALUE_SEPARATORS = " ,";
-
- public static final char HOME_TILDE_CHAR = '~';
- public static final char PATH_MACRO_CHAR = '%';
- public static final char LOCAL_HOME_MACRO = 'd';
- public static final char LOCAL_USER_MACRO = 'u';
- public static final char LOCAL_HOST_MACRO = 'l';
- public static final char REMOTE_HOST_MACRO = 'h';
- public static final char REMOTE_USER_MACRO = 'r';
- // Extra - not part of the standard
- public static final char REMOTE_PORT_MACRO = 'p';
-
- private static final class LazyDefaultConfigFileHolder {
- private static final Path CONFIG_FILE =
- PublicKeyEntry.getDefaultKeysFolderPath().resolve(STD_CONFIG_FILENAME);
-
- private LazyDefaultConfigFileHolder() {
- throw new UnsupportedOperationException("No instance allowed");
- }
- }
-
- private String host;
- private String hostName;
- private int port;
- private String username;
- private Boolean exclusiveIdentites;
- private Collection<String> identities = Collections.emptyList();
- private Map<String, String> properties = Collections.emptyMap();
-
- public HostConfigEntry() {
- super();
- }
-
- public HostConfigEntry(String pattern, String host, int port, String username) {
- setHost(pattern);
- setHostName(host);
- setPort(port);
- setUsername(username);
- }
-
- /**
- * @return The <U>pattern(s)</U> represented by this entry
- */
- public String getHost() {
- return host;
- }
-
- public void setHost(String host) {
- this.host = host;
- setPatterns(parsePatterns(parseConfigValue(host)));
- }
-
- public void setHost(Collection<String> patterns) {
- this.host = GenericUtils.join(ValidateUtils.checkNotNullAndNotEmpty(patterns, "No patterns"), ',');
- setPatterns(parsePatterns(patterns));
- }
-
- /**
- * @return The effective host name to connect to if the pattern matches
- */
- public String getHostName() {
- return hostName;
- }
-
- public void setHostName(String hostName) {
- this.hostName = hostName;
- }
-
- public String resolveHostName(String originalHost) {
- return resolveHostName(originalHost, getHostName());
- }
-
- /**
- * @return A port override - if positive
- */
- public int getPort() {
- return port;
- }
-
- public void setPort(int port) {
- this.port = port;
- }
-
- /**
- * Resolves the effective port to use
- *
- * @param originalPort The original requested port
- * @return If the host entry port is positive, then it is used, otherwise
- * the original requested port
- * @see #resolvePort(int, int)
- */
- public int resolvePort(int originalPort) {
- return resolvePort(originalPort, getPort());
- }
-
- /**
- * @return A username override - if not {@code null}/empty
- */
- @Override
- public String getUsername() {
- return username;
- }
-
- @Override
- public void setUsername(String username) {
- this.username = username;
- }
-
- /**
- * Resolves the effective username
- *
- * @param originalUser The original requested username
- * @return If the configured host entry username is not {@code null}/empty
- * then it is used, otherwise the original one.
- * @see #resolveUsername(String)
- */
- public String resolveUsername(String originalUser) {
- return resolveUsername(originalUser, getUsername());
- }
-
- /**
- * @return The current identities file paths - may be {@code null}/empty
- */
- public Collection<String> getIdentities() {
- return identities;
- }
-
- /**
- * @param file A {@link File} that contains an identity key - never {@code null}
- */
- public void addIdentity(File file) {
- addIdentity(Objects.requireNonNull(file, "No file").toPath());
- }
-
- /**
- * @param path A {@link Path} to a file that contains an identity key
- * - never {@code null}
- */
- public void addIdentity(Path path) {
- addIdentity(Objects.requireNonNull(path, "No path").toAbsolutePath().normalize().toString());
- }
-
- /**
- * Adds a path to an identity file
- *
- * @param id The identity path to add - never {@code null}
- */
- public void addIdentity(String id) {
- String path = ValidateUtils.checkNotNullAndNotEmpty(id, "No identity provided");
- if (GenericUtils.isEmpty(identities)) {
- identities = new LinkedList<>();
- }
- identities.add(path);
- }
-
- public void setIdentities(Collection<String> identities) {
- this.identities = (identities == null) ? Collections.emptyList() : identities;
- }
-
- /**
- * @return {@code true} if must use only the identities in this entry
- */
- public boolean isIdentitiesOnly() {
- return (exclusiveIdentites == null) ? DEFAULT_EXCLUSIVE_IDENTITIES : exclusiveIdentites;
- }
-
- public void setIdentitiesOnly(boolean identitiesOnly) {
- exclusiveIdentites = identitiesOnly;
- }
-
- /**
- * @return A {@link Map} of extra properties that have been read - may be
- * {@code null}/empty, or even contain some values that have been parsed
- * and set as members of the entry (e.g., host, port, etc.). <B>Note:</B>
- * multi-valued keys use a comma-separated list of values
- */
- public Map<String, String> getProperties() {
- return properties;
- }
-
- /**
- * @param name Property name - never {@code null}/empty
- * @return Property value or {@code null} if no such property
- * @see #getProperty(String, String)
- */
- public String getProperty(String name) {
- return getProperty(name, null);
- }
-
- /**
- * @param name Property name - never {@code null}/empty
- * @param defaultValue Default value to return if no such property
- * @return The property value or the default one if no such property
- */
- public String getProperty(String name, String defaultValue) {
- String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
- Map<String, String> props = getProperties();
- if (GenericUtils.isEmpty(props)) {
- return null;
- }
-
- String value = props.get(key);
- if (GenericUtils.isEmpty(value)) {
- return defaultValue;
- } else {
- return value;
- }
- }
-
- /**
- * Updates the values that are <U>not</U> already configured with those
- * from the global entry
- *
- * @param globalEntry The global entry - ignored if {@code null} or
- * same reference as this entry
- * @return {@code true} if anything updated
- */
- public boolean processGlobalValues(HostConfigEntry globalEntry) {
- if ((globalEntry == null) || (this == globalEntry)) {
- return false;
- }
-
- boolean modified = false;
- /*
- * NOTE !!! DO NOT TRY TO CHANGE THE ORDER OF THE OR-ing AS IT
- * WOULD CAUSE INVALID CODE EXECUTION
- */
- modified = updateGlobalPort(globalEntry.getPort()) || modified;
- modified = updateGlobalHostName(globalEntry.getHostName()) || modified;
- modified = updateGlobalUserName(globalEntry.getUsername()) || modified;
- modified = updateGlobalIdentities(globalEntry.getIdentities()) || modified;
- modified = updateGlobalIdentityOnly(globalEntry.isIdentitiesOnly()) || modified;
-
- Map<String, String> updated = updateGlobalProperties(globalEntry.getProperties());
- modified = (GenericUtils.size(updated) > 0) || modified;
-
- return modified;
- }
-
- /**
- * Sets all the properties for which no current value exists in the entry
- *
- * @param props The global properties - ignored if {@code null}/empty
- * @return A {@link Map} of the <U>updated</U> properties
- */
- public Map<String, String> updateGlobalProperties(Map<String, String> props) {
- if (GenericUtils.isEmpty(props)) {
- return Collections.emptyMap();
- }
-
- Map<String, String> updated = null;
- // Cannot use forEach because of the modification of the updated map value (non-final)
- for (Map.Entry<String, String> pe : props.entrySet()) {
- String key = pe.getKey();
- String curValue = getProperty(key);
- if (GenericUtils.length(curValue) > 0) {
- continue;
- }
-
- String newValue = pe.getValue();
- setProperty(key, newValue);
-
- if (updated == null) {
- updated = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- }
-
- updated.put(key, newValue);
- }
-
- if (updated == null) {
- return Collections.emptyMap();
- } else {
- return updated;
- }
- }
-
- /**
- * @param ids Global identities - ignored if {@code null}/empty or already
- * have configured identities
- * @return {@code true} if updated identities
- */
- public boolean updateGlobalIdentities(Collection<String> ids) {
- if (GenericUtils.isEmpty(ids) || (GenericUtils.size(getIdentities()) > 0)) {
- return false;
- }
-
- for (String id : ids) {
- addIdentity(id);
- }
-
- return true;
- }
-
- /**
- * @param user The global user name - ignored if {@code null}/empty or
- * already have a configured user
- * @return {@code true} if updated the username
- */
- public boolean updateGlobalUserName(String user) {
- if (GenericUtils.isEmpty(user) || (GenericUtils.length(getUsername()) > 0)) {
- return false;
- }
-
- setUsername(user);
- return true;
- }
-
- /**
- * @param name The global host name - ignored if {@code null}/empty or
- * already have a configured target host
- * @return {@code true} if updated the target host
- */
- public boolean updateGlobalHostName(String name) {
- if (GenericUtils.isEmpty(name) || (GenericUtils.length(getHostName()) > 0)) {
- return false;
- }
-
- setHostName(name);
- return true;
- }
-
- /**
- * @param portValue The global port value - ignored if not positive
- * or already have a configured port
- * @return {@code true} if updated the port value
- */
- public boolean updateGlobalPort(int portValue) {
- if ((portValue <= 0) || (getPort() > 0)) {
- return false;
- }
-
- setPort(portValue);
- return true;
- }
-
- /**
- * @param identitiesOnly Whether to use only the identities in this entry.
- * Ignored if already set
- * @return {@code true} if updated the option value
- */
- public boolean updateGlobalIdentityOnly(boolean identitiesOnly) {
- if (exclusiveIdentites != null) {
- return false;
- }
-
- setIdentitiesOnly(identitiesOnly);
- return true;
- }
-
- /**
- * @param name Property name - never {@code null}/empty
- * @param valsList The available values for the property
- * @param ignoreAlreadyInitialized If {@code false} and one of the "known"
- * properties is encountered then throws an exception
- * @throws IllegalArgumentException If an existing value is overwritten and
- * <tt>ignoreAlreadyInitialized</tt> is {@code false} (except for {@link #IDENTITY_FILE_CONFIG_PROP}
- * which is <U>cumulative</U>
- * @see #HOST_NAME_CONFIG_PROP
- * @see #PORT_CONFIG_PROP
- * @see #USER_CONFIG_PROP
- * @see #IDENTITY_FILE_CONFIG_PROP
- */
- public void processProperty(String name, Collection<String> valsList, boolean ignoreAlreadyInitialized) {
- String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
- String joinedValue = GenericUtils.join(valsList, ',');
- appendPropertyValue(key, joinedValue);
-
- if (HOST_NAME_CONFIG_PROP.equalsIgnoreCase(key)) {
- ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target hosts N/A: %s", joinedValue);
-
- String curValue = getHostName();
- ValidateUtils.checkTrue(GenericUtils.isEmpty(curValue) || ignoreAlreadyInitialized, "Already initialized %s: %s", key, curValue);
- setHostName(joinedValue);
- } else if (PORT_CONFIG_PROP.equalsIgnoreCase(key)) {
- ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target ports N/A: %s", joinedValue);
-
- int curValue = getPort();
- ValidateUtils.checkTrue((curValue <= 0) || ignoreAlreadyInitialized, "Already initialized %s: %d", key, curValue);
-
- int newValue = Integer.parseInt(joinedValue);
- ValidateUtils.checkTrue(newValue > 0, "Bad new port value: %d", newValue);
- setPort(newValue);
- } else if (USER_CONFIG_PROP.equalsIgnoreCase(key)) {
- ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target users N/A: %s", joinedValue);
-
- String curValue = getUsername();
- ValidateUtils.checkTrue(GenericUtils.isEmpty(curValue) || ignoreAlreadyInitialized, "Already initialized %s: %s", key, curValue);
- setUsername(joinedValue);
- } else if (IDENTITY_FILE_CONFIG_PROP.equalsIgnoreCase(key)) {
- ValidateUtils.checkTrue(GenericUtils.size(valsList) > 0, "No identity files specified");
- for (String id : valsList) {
- addIdentity(id);
- }
- } else if (EXCLUSIVE_IDENTITIES_CONFIG_PROP.equalsIgnoreCase(key)) {
- setIdentitiesOnly(
- SshConfigFileReader.parseBooleanValue(
- ValidateUtils.checkNotNullAndNotEmpty(joinedValue, "No identities option value")));
- }
- }
-
- /**
- * Appends a value using a <U>comma</U> to an existing one. If no previous
- * value then same as calling {@link #setProperty(String, String)}.
- *
- * @param name Property name - never {@code null}/empty
- * @param value The value to be appended - ignored if {@code null}/empty
- * @return The value <U>before</U> appending - {@code null} if no previous value
- */
- public String appendPropertyValue(String name, String value) {
- String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
- String curVal = getProperty(key);
- if (GenericUtils.isEmpty(value)) {
- return curVal;
- }
-
- if (GenericUtils.isEmpty(curVal)) {
- return setProperty(key, value);
- }
-
- return setProperty(key, curVal + ',' + value);
- }
-
- /**
- * Sets / Replaces the property value
- *
- * @param name Property name - never {@code null}/empty
- * @param value Property value - if {@code null}/empty then
- * {@link #removeProperty(String)} is called
- * @return The previous property value - {@code null} if no such name
- */
- public String setProperty(String name, String value) {
- if (GenericUtils.isEmpty(value)) {
- return removeProperty(name);
- }
-
- String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
- if (GenericUtils.isEmpty(properties)) {
- properties = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- }
-
- return properties.put(key, value);
- }
-
- /**
- * @param name Property name - never {@code null}/empty
- * @return The removed property value - {@code null} if no such property name
- */
- public String removeProperty(String name) {
- String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
- Map<String, String> props = getProperties();
- if (GenericUtils.isEmpty(props)) {
- return null;
- } else {
- return props.remove(key);
- }
- }
-
- /**
- * @param properties The properties to set - if {@code null} then an empty
- * map is effectively set. <B>Note:</B> it is highly recommended to use a
- * <U>case insensitive</U> key mapper.
- */
- public void setProperties(Map<String, String> properties) {
- this.properties = (properties == null) ? Collections.emptyMap() : properties;
- }
-
- public <A extends Appendable> A append(A sb) throws IOException {
- sb.append(HOST_CONFIG_PROP).append(' ').append(ValidateUtils.checkNotNullAndNotEmpty(getHost(), "No host pattern")).append(IoUtils.EOL);
- appendNonEmptyProperty(sb, HOST_NAME_CONFIG_PROP, getHostName());
- appendNonEmptyPort(sb, PORT_CONFIG_PROP, getPort());
- appendNonEmptyProperty(sb, USER_CONFIG_PROP, getUsername());
- appendNonEmptyValues(sb, IDENTITY_FILE_CONFIG_PROP, getIdentities());
- if (exclusiveIdentites != null) {
- appendNonEmptyProperty(sb, EXCLUSIVE_IDENTITIES_CONFIG_PROP, SshConfigFileReader.yesNoValueOf(exclusiveIdentites));
- }
- appendNonEmptyProperties(sb, getProperties());
- return sb;
- }
-
- @Override
- public String toString() {
- return getHost() + ": " + getUsername() + "@" + getHostName() + ":" + getPort();
- }
-
- /**
- * @param <A> The {@link Appendable} type
- * @param sb The target appender
- * @param name The property name - never {@code null}/empty
- * @param port The port value - ignored if non-positive
- * @return The target appender after having appended (or not) the value
- * @throws IOException If failed to append the requested data
- * @see #appendNonEmptyProperty(Appendable, String, Object)
- */
- public static <A extends Appendable> A appendNonEmptyPort(A sb, String name, int port) throws IOException {
- return appendNonEmptyProperty(sb, name, (port > 0) ? Integer.toString(port) : null);
- }
-
- /**
- * Appends the extra properties - while skipping the {@link #EXPLICIT_PROPERTIES} ones
- *
- * @param <A> The {@link Appendable} type
- * @param sb The target appender
- * @param props The {@link Map} of properties - ignored if {@code null}/empty
- * @return The target appender after having appended (or not) the value
- * @throws IOException If failed to append the requested data
- * @see #appendNonEmptyProperty(Appendable, String, Object)
- */
- public static <A extends Appendable> A appendNonEmptyProperties(A sb, Map<String, ?> props) throws IOException {
- if (GenericUtils.isEmpty(props)) {
- return sb;
- }
-
- // Cannot use forEach because of the IOException being thrown by appendNonEmptyProperty
- for (Map.Entry<String, ?> pe : props.entrySet()) {
- String name = pe.getKey();
- if (EXPLICIT_PROPERTIES.contains(name)) {
- continue;
- }
-
- appendNonEmptyProperty(sb, name, pe.getValue());
- }
-
- return sb;
- }
-
- /**
- * @param <A> The {@link Appendable} type
- * @param sb The target appender
- * @param name The property name - never {@code null}/empty
- * @param value The property value - ignored if {@code null}. <B>Note:</B>
- * if the string representation of the value contains any commas, they are
- * assumed to indicate a multi-valued property which is broken down to
- * <U>individual</U> lines - one per value.
- * @return The target appender after having appended (or not) the value
- * @throws IOException If failed to append the requested data
- * @see #appendNonEmptyValues(Appendable, String, Object...)
- */
- public static <A extends Appendable> A appendNonEmptyProperty(A sb, String name, Object value) throws IOException {
- String s = Objects.toString(value, null);
- String[] vals = GenericUtils.split(s, ',');
- return appendNonEmptyValues(sb, name, (Object[]) vals);
- }
-
- /**
- * @param <A> The {@link Appendable} type
- * @param sb The target appender
- * @param name The property name - never {@code null}/empty
- * @param values The values to be added - one per line - ignored if {@code null}/empty
- * @return The target appender after having appended (or not) the value
- * @throws IOException If failed to append the requested data
- * @see #appendNonEmptyValues(Appendable, String, Collection)
- */
- public static <A extends Appendable> A appendNonEmptyValues(A sb, String name, Object... values) throws IOException {
- return appendNonEmptyValues(sb, name, GenericUtils.isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
- }
-
- /**
- * @param <A> The {@link Appendable} type
- * @param sb The target appender
- * @param name The property name - never {@code null}/empty
- * @param values The values to be added - one per line - ignored if {@code null}/empty
- * @return The target appender after having appended (or not) the value
- * @throws IOException If failed to append the requested data
- */
- public static <A extends Appendable> A appendNonEmptyValues(A sb, String name, Collection<?> values) throws IOException {
- String k = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
- if (GenericUtils.isEmpty(values)) {
- return sb;
- }
-
- for (Object v : values) {
- sb.append(" ").append(k).append(' ').append(Objects.toString(v)).append(IoUtils.EOL);
- }
-
- return sb;
- }
-
- /**
- * @param entries The entries - ignored if {@code null}/empty
- * @return A {@link HostConfigEntryResolver} wrapper using the entries
- */
- public static HostConfigEntryResolver toHostConfigEntryResolver(final Collection<? extends HostConfigEntry> entries) {
- if (GenericUtils.isEmpty(entries)) {
- return HostConfigEntryResolver.EMPTY;
- } else {
- return (host1, port1, username1) -> {
- List<HostConfigEntry> matches = findMatchingEntries(host1, entries);
- int numMatches = GenericUtils.size(matches);
- if (numMatches <= 0) {
- return null;
- }
-
- HostConfigEntry match = (numMatches == 1) ? matches.get(0) : findBestMatch(matches);
- if (match == null) {
- ValidateUtils.throwIllegalArgumentException("No best match found for %s@%s:%d out of %d matches", username1, host1, port1, numMatches);
- }
-
- return normalizeEntry(match, host1, port1, username1);
- };
- }
- }
-
- /**
- * @param entry The original entry - ignored if {@code null}
- * @param host The original host name / address
- * @param port The original port
- * @param username The original user name
- * @return A <U>cloned</U> entry whose values are resolved - including
- * expanding macros in the identities files
- * @throws IOException If failed to normalize the entry
- * @see #resolveHostName(String)
- * @see #resolvePort(int)
- * @see #resolveUsername(String)
- * @see #resolveIdentityFilePath(String, String, int, String)
- */
- public static HostConfigEntry normalizeEntry(HostConfigEntry entry, String host, int port, String username) throws IOException {
- if (entry == null) {
- return null;
- }
-
- HostConfigEntry normal = new HostConfigEntry();
- normal.setHost(host);
- normal.setHostName(entry.resolveHostName(host));
- normal.setPort(entry.resolvePort(port));
- normal.setUsername(entry.resolveUsername(username));
-
- Map<String, String> props = entry.getProperties();
- if (GenericUtils.size(props) > 0) {
- normal.setProperties(new TreeMap<>(props));
- }
-
- Collection<String> ids = entry.getIdentities();
- if (GenericUtils.isEmpty(ids)) {
- return normal;
- }
-
- normal.setIdentities(Collections.emptyList()); // start fresh
- for (String id : ids) {
- String path = resolveIdentityFilePath(id, host, port, username);
- normal.addIdentity(path);
- }
-
- return normal;
- }
-
- /**
- * Resolves the effective target host
- *
- * @param originalName The original requested host
- * @param entryName The configured host
- * @return If the configured host entry is not {@code null}/empty
- * then it is used, otherwise the original one.
- */
- public static String resolveHostName(String originalName, String entryName) {
- if (GenericUtils.isEmpty(entryName)) {
- return originalName;
- } else {
- return entryName;
- }
- }
-
- /**
- * Resolves the effective username
- *
- * @param originalUser The original requested username
- * @param entryUser The configured host entry username
- * @return If the configured host entry username is not {@code null}/empty
- * then it is used, otherwise the original one.
- */
- public static String resolveUsername(String originalUser, String entryUser) {
- if (GenericUtils.isEmpty(entryUser)) {
- return originalUser;
- } else {
- return entryUser;
- }
- }
-
- /**
- * Resolves the effective port to use
- *
- * @param originalPort The original requested port
- * @param entryPort The configured host entry port
- * @return If the host entry port is positive, then it is used, otherwise
- * the original requested port
- */
- public static int resolvePort(int originalPort, int entryPort) {
- if (entryPort <= 0) {
- return originalPort;
- } else {
- return entryPort;
- }
- }
-
- public static List<HostConfigEntry> readHostConfigEntries(File file) throws IOException {
- return readHostConfigEntries(file.toPath(), IoUtils.EMPTY_OPEN_OPTIONS);
- }
-
- public static List<HostConfigEntry> readHostConfigEntries(Path path, OpenOption... options) throws IOException {
- try (InputStream input = Files.newInputStream(path, options)) {
- return readHostConfigEntries(input, true);
- }
- }
-
- public static List<HostConfigEntry> readHostConfigEntries(URL url) throws IOException {
- try (InputStream input = url.openStream()) {
- return readHostConfigEntries(input, true);
- }
- }
-
- public static List<HostConfigEntry> readHostConfigEntries(String filePath) throws IOException {
- try (InputStream inStream = new FileInputStream(filePath)) {
- return readHostConfigEntries(inStream, true);
- }
- }
-
- public static List<HostConfigEntry> readHostConfigEntries(InputStream inStream, boolean okToClose) throws IOException {
- try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(inStream, okToClose), StandardCharsets.UTF_8)) {
- return readHostConfigEntries(reader, true);
- }
- }
-
- public static List<HostConfigEntry> readHostConfigEntries(Reader rdr, boolean okToClose) throws IOException {
- try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(rdr, okToClose))) {
- return readHostConfigEntries(buf);
- }
- }
-
- /**
- * Reads configuration entries
- *
- * @param rdr The {@link BufferedReader} to use
- * @return The {@link List} of read {@link HostConfigEntry}-ies
- * @throws IOException If failed to parse the read configuration
- */
- public static List<HostConfigEntry> readHostConfigEntries(BufferedReader rdr) throws IOException {
- HostConfigEntry curEntry = null;
- HostConfigEntry globalEntry = null;
- List<HostConfigEntry> entries = null;
-
- 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(SshConfigFileReader.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 configuration value delimiter at line " + lineNumber + ": " + line);
- }
-
- String key = line.substring(0, pos);
- String value = line.substring(pos + 1);
- List<String> valsList = parseConfigValue(value);
-
- if (HOST_CONFIG_PROP.equalsIgnoreCase(key)) {
- if (GenericUtils.isEmpty(valsList)) {
- throw new StreamCorruptedException("Missing host pattern(s) at line " + lineNumber + ": " + line);
- }
-
- // If the all-hosts pattern is used, make sure no global section already active
- for (String name : valsList) {
- if (ALL_HOSTS_PATTERN.equalsIgnoreCase(name) && (globalEntry != null)) {
- throw new StreamCorruptedException("Overriding the global section with a specific one at line " + lineNumber + ": " + line);
- }
- }
-
- if (curEntry != null) {
- curEntry.processGlobalValues(globalEntry);
- }
-
- entries = updateEntriesList(entries, curEntry);
-
- curEntry = new HostConfigEntry();
- curEntry.setHost(valsList);
- } else if (curEntry == null) {
- // if 1st encountered property is NOT for a specific host, then configuration applies to ALL
- curEntry = new HostConfigEntry();
- curEntry.setHost(Collections.singletonList(ALL_HOSTS_PATTERN));
- globalEntry = curEntry;
- }
-
- try {
- curEntry.processProperty(key, valsList, false);
- } catch (RuntimeException e) {
- throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
- + " to process line #" + lineNumber + " (" + line + ")"
- + ": " + e.getMessage());
- }
- }
-
- if (curEntry != null) {
- curEntry.processGlobalValues(globalEntry);
- }
-
- entries = updateEntriesList(entries, curEntry);
- if (entries == null) {
- return Collections.emptyList();
- } else {
- return entries;
- }
- }
-
- /**
- * Finds the best match out of the given ones.
- *
- * @param matches The available matches - ignored if {@code null}/empty
- * @return The best match or {@code null} if no matches or no best match found
- * @see #findBestMatch(Iterator)
- */
- public static HostConfigEntry findBestMatch(Collection<? extends HostConfigEntry> matches) {
- if (GenericUtils.isEmpty(matches)) {
- return null;
- } else {
- return findBestMatch(matches.iterator());
- }
- }
-
- /**
- * Finds the best match out of the given ones.
- *
- * @param matches The available matches - ignored if {@code null}/empty
- * @return The best match or {@code null} if no matches or no best match found
- * @see #findBestMatch(Iterator)
- */
- public static HostConfigEntry findBestMatch(Iterable<? extends HostConfigEntry> matches) {
- if (matches == null) {
- return null;
- } else {
- return findBestMatch(matches.iterator());
- }
- }
-
- /**
- * Finds the best match out of the given ones. The best match is defined as one whose
- * pattern is as <U>specific</U> as possible (if more than one match is available).
- * I.e., a non-global match is preferred over global one, and a match with no wildcards
- * is preferred over one with such a pattern.
- *
- * @param matches The available matches - ignored if {@code null}/empty
- * @return The best match or {@code null} if no matches or no best match found
- * @see #isSpecificHostPattern(String)
- */
- public static HostConfigEntry findBestMatch(Iterator<? extends HostConfigEntry> matches) {
- if ((matches == null) || (!matches.hasNext())) {
- return null;
- }
-
- HostConfigEntry candidate = matches.next();
- int wildcardMatches = 0;
- while (matches.hasNext()) {
- HostConfigEntry entry = matches.next();
- String entryPattern = entry.getHost();
- String candidatePattern = candidate.getHost();
- // prefer non-global entry over global entry
- if (ALL_HOSTS_PATTERN.equalsIgnoreCase(candidatePattern)) {
- // unlikely, but handle it
- if (ALL_HOSTS_PATTERN.equalsIgnoreCase(entryPattern)) {
- wildcardMatches++;
- } else {
- candidate = entry;
- wildcardMatches = 0;
- }
- continue;
- }
-
- if (isSpecificHostPattern(entryPattern)) {
- // if both are specific then no best match
- if (isSpecificHostPattern(candidatePattern)) {
- return null;
- }
-
- candidate = entry;
- wildcardMatches = 0;
- continue;
- }
-
- wildcardMatches++;
- }
-
- String candidatePattern = candidate.getHost();
- // best match either has specific host or no wildcard matches
- if ((wildcardMatches <= 0) || (isSpecificHostPattern(candidatePattern))) {
- return candidate;
- }
-
- return null;
- }
-
- public static List<HostConfigEntry> updateEntriesList(List<HostConfigEntry> entries, HostConfigEntry curEntry) {
- if (curEntry == null) {
- return entries;
- }
-
- if (entries == null) {
- entries = new ArrayList<>();
- }
-
- entries.add(curEntry);
- return entries;
- }
-
- public static void writeHostConfigEntries(File file, Collection<? extends HostConfigEntry> entries) throws IOException {
- writeHostConfigEntries(Objects.requireNonNull(file, "No file").toPath(), entries, IoUtils.EMPTY_OPEN_OPTIONS);
- }
-
- public static void writeHostConfigEntries(Path path, Collection<? extends HostConfigEntry> entries, OpenOption... options) throws IOException {
- try (OutputStream outputStream = Files.newOutputStream(path, options)) {
- writeHostConfigEntries(outputStream, true, entries);
- }
- }
-
- public static void writeHostConfigEntries(OutputStream outputStream, boolean okToClose, Collection<? extends HostConfigEntry> entries) throws IOException {
- if (GenericUtils.isEmpty(entries)) {
- return;
- }
-
- try (Writer w = new OutputStreamWriter(NoCloseOutputStream.resolveOutputStream(outputStream, okToClose), StandardCharsets.UTF_8)) {
- appendHostConfigEntries(w, entries);
- }
- }
-
- public static <A extends Appendable> A appendHostConfigEntries(A sb, Collection<? extends HostConfigEntry> entries) throws IOException {
- if (GenericUtils.isEmpty(entries)) {
- return sb;
- }
-
- for (HostConfigEntry entry : entries) {
- entry.append(sb);
- }
-
- return sb;
- }
-
- /**
- * Checks if this is a multi-value - allow space and comma
- *
- * @param value The value - ignored if {@code null}/empty (after trimming)
- * @return A {@link List} of the encountered values
- */
- public static List<String> parseConfigValue(String value) {
- String s = GenericUtils.replaceWhitespaceAndTrim(value);
- if (GenericUtils.isEmpty(s)) {
- return Collections.emptyList();
- }
-
- for (int index = 0; index < MULTI_VALUE_SEPARATORS.length(); index++) {
- char sep = MULTI_VALUE_SEPARATORS.charAt(index);
- int pos = s.indexOf(sep);
- if (pos >= 0) {
- String[] vals = GenericUtils.split(s, sep);
- if (GenericUtils.isEmpty(vals)) {
- return Collections.emptyList();
- } else {
- return Arrays.asList(vals);
- }
- }
- }
-
- // this point is reached if no separators found
- return Collections.singletonList(s);
- }
-
- // The file name may use the tilde syntax to refer to a user’s home directory or one of the following escape characters:
- // '%d' (local user's home directory), '%u' (local user name), '%l' (local host name), '%h' (remote host name) or '%r' (remote user name).
- public static String resolveIdentityFilePath(String id, String host, int port, String username) throws IOException {
- if (GenericUtils.isEmpty(id)) {
- return id;
- }
-
- String path = id.replace('/', File.separatorChar); // make sure all separators are local
- String[] elements = GenericUtils.split(path, File.separatorChar);
- StringBuilder sb = new StringBuilder(path.length() + Long.SIZE);
- for (int index = 0; index < elements.length; index++) {
- String elem = elements[index];
- if (index > 0) {
- sb.append(File.separatorChar);
- }
-
- for (int curPos = 0; curPos < elem.length(); curPos++) {
- char ch = elem.charAt(curPos);
- if (ch == HOME_TILDE_CHAR) {
- ValidateUtils.checkTrue((curPos == 0) && (index == 0), "Home tilde must be first: %s", id);
- appendUserHome(sb);
- } else if (ch == PATH_MACRO_CHAR) {
- curPos++;
- ValidateUtils.checkTrue(curPos < elem.length(), "Missing macro modifier in %s", id);
- ch = elem.charAt(curPos);
- switch(ch) {
- case PATH_MACRO_CHAR:
- sb.append(ch);
- break;
- case LOCAL_HOME_MACRO:
- ValidateUtils.checkTrue((curPos == 1) && (index == 0), "Home macro must be first: %s", id);
- appendUserHome(sb);
- break;
- case LOCAL_USER_MACRO:
- sb.append(ValidateUtils.checkNotNullAndNotEmpty(OsUtils.getCurrentUser(), "No local user name value"));
- break;
- case LOCAL_HOST_MACRO: {
- InetAddress address = Objects.requireNonNull(InetAddress.getLocalHost(), "No local address");
- sb.append(ValidateUtils.checkNotNullAndNotEmpty(address.getHostName(), "No local name"));
- break;
- }
- case REMOTE_HOST_MACRO:
- sb.append(ValidateUtils.checkNotNullAndNotEmpty(host, "No remote host provided"));
- break;
- case REMOTE_USER_MACRO:
- sb.append(ValidateUtils.checkNotNullAndNotEmpty(username, "No remote user provided"));
- break;
- case REMOTE_PORT_MACRO:
- ValidateUtils.checkTrue(port > 0, "Bad remote port value: %d", port);
- sb.append(port);
- break;
- default:
- ValidateUtils.throwIllegalArgumentException("Bad modifier '%s' in %s", String.valueOf(ch), id);
- }
- } else {
- sb.append(ch);
- }
- }
- }
-
- return sb.toString();
- }
-
- public static StringBuilder appendUserHome(StringBuilder sb) {
- return appendUserHome(sb, IdentityUtils.getUserHomeFolder());
- }
-
- public static StringBuilder appendUserHome(StringBuilder sb, Path userHome) {
- return appendUserHome(sb, Objects.requireNonNull(userHome, "No user home folder").toString());
- }
-
- public static StringBuilder appendUserHome(StringBuilder sb, String userHome) {
- if (GenericUtils.isEmpty(userHome)) {
- return sb;
- }
-
- sb.append(userHome);
- // strip any ending separator since we add our own
- int len = sb.length();
- if (sb.charAt(len - 1) == File.separatorChar) {
- sb.setLength(len - 1);
- }
-
- return sb;
- }
-
- /**
- * @return The default {@link Path} location of the OpenSSH hosts entries configuration file
- */
- @SuppressWarnings("synthetic-access")
- public static Path getDefaultHostConfigFile() {
- return LazyDefaultConfigFileHolder.CONFIG_FILE;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
deleted file mode 100644
index a07cfcf..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
+++ /dev/null
@@ -1,60 +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.client.config.hosts;
-
-import java.io.IOException;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface HostConfigEntryResolver {
-
- /**
- * An "empty" implementation that does not resolve any entry
- */
- HostConfigEntryResolver EMPTY = new HostConfigEntryResolver() {
- @Override
- public HostConfigEntry resolveEffectiveHost(String host, int port, String username) throws IOException {
- return null;
- }
-
- @Override
- public String toString() {
- return "EMPTY";
- }
- };
-
- /**
- * Invoked when creating a new client session in order to allow for overriding
- * of the original parameters
- *
- * @param host The requested host - never {@code null}/empty
- * @param port The requested port
- * @param username The requested username
- * @return A {@link HostConfigEntry} for the actual target - {@code null} if use
- * original parameters. <B>Note:</B> if any identity files are attached to the
- * configuration then they must point to <U>existing</U> locations. This means
- * that any macros such as <code>~, %d, %h</code>, etc. must be resolved <U>prior</U>
- * to returning the value
- * @throws IOException If failed to resolve the configuration
- */
- HostConfigEntry resolveEffectiveHost(String host, int port, String username) throws IOException;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
deleted file mode 100644
index 20d682f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
+++ /dev/null
@@ -1,97 +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.client.config.hosts;
-
-import java.util.regex.Pattern;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * Represents a pattern definition in the <U>known_hosts</U> file
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="https://en.wikibooks.org/wiki/OpenSSH/Client_Configuration_Files#About_the_Contents_of_the_known_hosts_Files">
- * OpenSSH cookbook - About the Contents of the known hosts Files</A>
- */
-public class HostPatternValue {
- private Pattern pattern;
- private int port;
- private boolean negated;
-
- public HostPatternValue() {
- super();
- }
-
- public HostPatternValue(Pattern pattern, boolean negated) {
- this(pattern, 0, negated);
- }
-
- public HostPatternValue(Pattern pattern, int port, boolean negated) {
- this.pattern = pattern;
- this.port = port;
- this.negated = negated;
- }
-
- public Pattern getPattern() {
- return pattern;
- }
-
- public void setPattern(Pattern pattern) {
- this.pattern = pattern;
- }
-
- public int getPort() {
- return port;
- }
-
- public void setPort(int port) {
- this.port = port;
- }
-
- public boolean isNegated() {
- return negated;
- }
-
- public void setNegated(boolean negated) {
- this.negated = negated;
- }
-
- @Override
- public String toString() {
- Pattern p = getPattern();
- String purePattern = (p == null) ? null : p.pattern();
- StringBuilder sb = new StringBuilder(GenericUtils.length(purePattern) + Short.SIZE);
- if (isNegated()) {
- sb.append(HostPatternsHolder.NEGATION_CHAR_PATTERN);
- }
-
- int portValue = getPort();
- if (portValue > 0) {
- sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM);
- }
- sb.append(purePattern);
- if (portValue > 0) {
- sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM);
- sb.append(HostPatternsHolder.PORT_VALUE_DELIMITER);
- sb.append(portValue);
- }
-
- return sb.toString();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
deleted file mode 100644
index 9d90dac..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
+++ /dev/null
@@ -1,343 +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.client.config.hosts;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-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 abstract class HostPatternsHolder {
-
- /**
- * Used in a host pattern to denote zero or more consecutive characters
- */
- public static final char WILDCARD_PATTERN = '*';
- public static final String ALL_HOSTS_PATTERN = String.valueOf(WILDCARD_PATTERN);
-
- /**
- * Used in a host pattern to denote any <U>one</U> character
- */
- public static final char SINGLE_CHAR_PATTERN = '?';
-
- /**
- * Used to negate a host pattern
- */
- public static final char NEGATION_CHAR_PATTERN = '!';
-
- /**
- * The available pattern characters
- */
- public static final String PATTERN_CHARS = new String(new char[]{WILDCARD_PATTERN, SINGLE_CHAR_PATTERN, NEGATION_CHAR_PATTERN});
-
- /** Port value separator if non-standard port pattern used */
- public static final char PORT_VALUE_DELIMITER = ':';
-
- /** Non-standard port specification host pattern enclosure start delimiter */
- public static final char NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM = '[';
-
- /** Non-standard port specification host pattern enclosure end delimiter */
- public static final char NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM = ']';
-
- private Collection<HostPatternValue> patterns = new LinkedList<>();
-
- protected HostPatternsHolder() {
- super();
- }
-
- public Collection<HostPatternValue> getPatterns() {
- return patterns;
- }
-
- public void setPatterns(Collection<HostPatternValue> patterns) {
- this.patterns = patterns;
- }
-
- /**
- * Checks if a given host name / address matches the entry's host pattern(s)
- *
- * @param host The host name / address - ignored if {@code null}/empty
- * @param port The connection port
- * @return {@code true} if the name / address matches the pattern(s)
- * @see #isHostMatch(String, Pattern)
- */
- public boolean isHostMatch(String host, int port) {
- return isHostMatch(host, port, getPatterns());
- }
-
- /**
- * @param pattern The pattern to check - ignored if {@code null}/empty
- * @return {@code true} if the pattern is not empty and contains no wildcard characters
- * @see #WILDCARD_PATTERN
- * @see #SINGLE_CHAR_PATTERN
- * @see #SINGLE_CHAR_PATTERN
- */
- public static boolean isSpecificHostPattern(String pattern) {
- if (GenericUtils.isEmpty(pattern)) {
- return false;
- }
-
- for (int index = 0; index < PATTERN_CHARS.length(); index++) {
- char ch = PATTERN_CHARS.charAt(index);
- if (pattern.indexOf(ch) >= 0) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Locates all the matching entries for a give host name / address
- *
- * @param host The host name / address - ignored if {@code null}/empty
- * @param entries The {@link HostConfigEntry}-ies to scan - ignored if {@code null}/empty
- * @return A {@link List} of all the matching entries
- * @see #isHostMatch(String, int)
- */
- public static List<HostConfigEntry> findMatchingEntries(String host, HostConfigEntry... entries) {
- // TODO in Java-8 use Stream(s) + predicate
- if (GenericUtils.isEmpty(host) || GenericUtils.isEmpty(entries)) {
- return Collections.emptyList();
- } else {
- return findMatchingEntries(host, Arrays.asList(entries));
- }
- }
-
- /**
- * Locates all the matching entries for a give host name / address
- *
- * @param host The host name / address - ignored if {@code null}/empty
- * @param entries The {@link HostConfigEntry}-ies to scan - ignored if {@code null}/empty
- * @return A {@link List} of all the matching entries
- * @see #isHostMatch(String, int)
- */
- public static List<HostConfigEntry> findMatchingEntries(String host, Collection<? extends HostConfigEntry> entries) {
- // TODO in Java-8 use Stream(s) + predicate
- if (GenericUtils.isEmpty(host) || GenericUtils.isEmpty(entries)) {
- return Collections.emptyList();
- }
-
- List<HostConfigEntry> matches = null;
- for (HostConfigEntry entry : entries) {
- if (!entry.isHostMatch(host, 0 /* any port */)) {
- continue; // debug breakpoint
- }
-
- if (matches == null) {
- matches = new ArrayList<>(entries.size()); // in case ALL of them match
- }
-
- matches.add(entry);
- }
-
- if (matches == null) {
- return Collections.emptyList();
- } else {
- return matches;
- }
- }
-
- public static boolean isHostMatch(String host, int port, Collection<HostPatternValue> patterns) {
- if (GenericUtils.isEmpty(patterns)) {
- return false;
- }
-
- boolean matchFound = false;
- for (HostPatternValue pv : patterns) {
- boolean negated = pv.isNegated();
- /*
- * If already found a match we are interested only in negations
- */
- if (matchFound && (!negated)) {
- continue;
- }
-
- if (!isHostMatch(host, pv.getPattern())) {
- continue;
- }
-
- /*
- * According to https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5:
- *
- * If a negated entry is matched, then the Host entry is ignored,
- * regardless of whether any other patterns on the line match.
- */
- if (negated) {
- return false;
- }
-
- int pvPort = pv.getPort();
- if ((pvPort != 0) && (port != 0) && (pvPort != port)) {
- continue;
- }
-
- matchFound = true;
- }
-
- return matchFound;
- }
-
- /**
- * Checks if a given host name / address matches a host pattern
- *
- * @param host The host name / address - ignored if {@code null}/empty
- * @param pattern The host {@link Pattern} - ignored if {@code null}
- * @return {@code true} if the name / address matches the pattern
- */
- public static boolean isHostMatch(String host, Pattern pattern) {
- if (GenericUtils.isEmpty(host) || (pattern == null)) {
- return false;
- }
-
- Matcher m = pattern.matcher(host);
- return m.matches();
- }
-
- public static List<HostPatternValue> parsePatterns(CharSequence... patterns) {
- return parsePatterns(GenericUtils.isEmpty(patterns) ? Collections.emptyList() : Arrays.asList(patterns));
- }
-
- public static List<HostPatternValue> parsePatterns(Collection<? extends CharSequence> patterns) {
- if (GenericUtils.isEmpty(patterns)) {
- return Collections.emptyList();
- }
-
- List<HostPatternValue> result = new ArrayList<>(patterns.size());
- for (CharSequence p : patterns) {
- result.add(ValidateUtils.checkNotNull(toPattern(p), "No pattern for %s", p));
- }
-
- return result;
- }
-
- /**
- * Converts a host pattern string to a regular expression matcher.
- * <B>Note:</B> pattern matching is <U>case insensitive</U>
- *
- * @param patternString The original pattern string - ignored if {@code null}/empty
- * @return The regular expression matcher {@link Pattern} and the indication
- * whether it is a negating pattern or not - {@code null} if no original string
- * @see #NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM
- * @see #NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM
- * @see #WILDCARD_PATTERN
- * @see #SINGLE_CHAR_PATTERN
- * @see #NEGATION_CHAR_PATTERN
- */
- public static HostPatternValue toPattern(CharSequence patternString) {
- String pattern = GenericUtils.replaceWhitespaceAndTrim(Objects.toString(patternString, null));
- if (GenericUtils.isEmpty(pattern)) {
- return null;
- }
-
- int patternLen = pattern.length();
- int port = 0;
- // Check if non-standard port value used
- StringBuilder sb = new StringBuilder(patternLen);
- if (pattern.charAt(0) == HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM) {
- int pos = GenericUtils.lastIndexOf(pattern, HostPatternsHolder.PORT_VALUE_DELIMITER);
- ValidateUtils.checkTrue(pos > 0, "Missing non-standard port value delimiter in %s", pattern);
- ValidateUtils.checkTrue(pos < (patternLen - 1), "Missing non-standard port value number in %s", pattern);
- ValidateUtils.checkTrue(pattern.charAt(pos - 1) == HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM,
- "Invalid non-standard port value host pattern enclosure delimiters in %s", pattern);
-
- String csPort = pattern.substring(pos + 1, patternLen);
- port = Integer.parseInt(csPort);
- ValidateUtils.checkTrue((port > 0) && (port <= 0xFFFF), "Invalid non-start port value (%d) in %s", port, pattern);
-
- pattern = pattern.substring(1, pos - 1);
- patternLen = pattern.length();
- }
-
- boolean negated = false;
- for (int curPos = 0; curPos < patternLen; curPos++) {
- char ch = pattern.charAt(curPos);
- ValidateUtils.checkTrue(isValidPatternChar(ch), "Invalid host pattern char in %s", pattern);
-
- switch(ch) {
- case '.': // need to escape it
- sb.append('\\').append(ch);
- break;
- case SINGLE_CHAR_PATTERN:
- sb.append('.');
- break;
- case WILDCARD_PATTERN:
- sb.append(".*");
- break;
- case NEGATION_CHAR_PATTERN:
- ValidateUtils.checkTrue(!negated, "Double negation in %s", pattern);
- ValidateUtils.checkTrue(curPos == 0, "Negation must be 1st char: %s", pattern);
- negated = true;
- break;
- default:
- sb.append(ch);
- }
- }
-
- return new HostPatternValue(Pattern.compile(sb.toString(), Pattern.CASE_INSENSITIVE), port, negated);
- }
-
- /**
- * Checks if the given character is valid for a host pattern. Valid
- * characters are:
- * <UL>
- * <LI>A-Z</LI>
- * <LI>a-z</LI>
- * <LI>0-9</LI>
- * <LI>Underscore (_)</LI>
- * <LI>Hyphen (-)</LI>
- * <LI>Dot (.)</LI>
- * <LI>The {@link #WILDCARD_PATTERN}</LI>
- * <LI>The {@link #SINGLE_CHAR_PATTERN}</LI>
- * </UL>
- *
- * @param ch The character to validate
- * @return {@code true} if valid pattern character
- */
- public static boolean isValidPatternChar(char ch) {
- if ((ch <= ' ') || (ch >= 0x7E)) {
- return false;
- }
- if ((ch >= 'a') && (ch <= 'z')) {
- return true;
- }
- if ((ch >= 'A') && (ch <= 'Z')) {
- return true;
- }
- if ((ch >= '0') && (ch <= '9')) {
- return true;
- }
- if ("-_.".indexOf(ch) >= 0) {
- return true;
- }
- return PATTERN_CHARS.indexOf(ch) >= 0;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
deleted file mode 100644
index 2d9a322..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
+++ /dev/null
@@ -1,66 +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.client.config.hosts;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Objects;
-import java.util.Set;
-
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.mac.BuiltinMacs;
-import org.apache.sshd.common.mac.Mac;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Available digesters for known hosts entries
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum KnownHostDigest implements NamedFactory<Mac> {
- SHA1("1", BuiltinMacs.hmacsha1);
-
- public static final Set<KnownHostDigest> VALUES =
- Collections.unmodifiableSet(EnumSet.allOf(KnownHostDigest.class));
-
- private final String name;
- private final Factory<Mac> factory;
-
- KnownHostDigest(String name, Factory<Mac> factory) {
- this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name");
- this.factory = Objects.requireNonNull(factory, "No factory");
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public Mac create() {
- return factory.create();
- }
-
- public static KnownHostDigest fromName(String name) {
- return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
deleted file mode 100644
index 4d4e97a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
+++ /dev/null
@@ -1,276 +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.client.config.hosts;
-
-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.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.config.SshConfigFileReader;
-import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
-import org.apache.sshd.common.config.keys.PublicKeyEntry;
-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;
-
-/**
- * Contains a representation of an entry in the <code>known_hosts</code> file
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="http://www.manpagez.com/man/8/sshd/">sshd(8) man page</A>
- */
-public class KnownHostEntry extends HostPatternsHolder {
- /**
- * Character that denotes that start of a marker
- */
- public static final char MARKER_INDICATOR = '@';
-
- /**
- * Standard OpenSSH config file name
- */
- public static final String STD_HOSTS_FILENAME = "known_hosts";
-
- private static final class LazyDefaultConfigFileHolder {
- private static final Path HOSTS_FILE =
- PublicKeyEntry.getDefaultKeysFolderPath().resolve(STD_HOSTS_FILENAME);
-
- private LazyDefaultConfigFileHolder() {
- throw new UnsupportedOperationException("No instance allowed");
- }
- }
-
- private String line;
- private String marker;
- private AuthorizedKeyEntry keyEntry;
- private KnownHostHashValue hashedEntry;
-
- public KnownHostEntry() {
- super();
- }
-
- /**
- * @param line The original line from which this entry was created
- */
- public KnownHostEntry(String line) {
- this.line = line;
- }
-
- /**
- * @return The original line from which this entry was created
- */
- public String getConfigLine() {
- return line;
- }
-
- public void setConfigLine(String line) {
- this.line = line;
- }
-
- public String getMarker() {
- return marker;
- }
-
- public void setMarker(String marker) {
- this.marker = marker;
- }
-
- public AuthorizedKeyEntry getKeyEntry() {
- return keyEntry;
- }
-
- public void setKeyEntry(AuthorizedKeyEntry keyEntry) {
- this.keyEntry = keyEntry;
- }
-
- public KnownHostHashValue getHashedEntry() {
- return hashedEntry;
- }
-
- public void setHashedEntry(KnownHostHashValue hashedEntry) {
- this.hashedEntry = hashedEntry;
- }
-
- @Override
- public boolean isHostMatch(String host, int port) {
- if (super.isHostMatch(host, port)) {
- return true;
- }
-
- KnownHostHashValue hash = getHashedEntry();
- return (hash != null) && hash.isHostMatch(host);
- }
-
- @Override
- public String toString() {
- return getConfigLine();
- }
-
- /**
- * @return The default {@link Path} location of the OpenSSH known hosts file
- */
- @SuppressWarnings("synthetic-access")
- public static Path getDefaultKnownHostsFile() {
- return LazyDefaultConfigFileHolder.HOSTS_FILE;
- }
-
- public static List<KnownHostEntry> readKnownHostEntries(File file) throws IOException {
- return readKnownHostEntries(file.toPath(), IoUtils.EMPTY_OPEN_OPTIONS);
- }
-
- public static List<KnownHostEntry> readKnownHostEntries(Path path, OpenOption... options) throws IOException {
- try (InputStream input = Files.newInputStream(path, options)) {
- return readKnownHostEntries(input, true);
- }
- }
-
- public static List<KnownHostEntry> readKnownHostEntries(URL url) throws IOException {
- try (InputStream input = url.openStream()) {
- return readKnownHostEntries(input, true);
- }
- }
-
- public static List<KnownHostEntry> readKnownHostEntries(String filePath) throws IOException {
- try (InputStream inStream = new FileInputStream(filePath)) {
- return readKnownHostEntries(inStream, true);
- }
- }
-
- public static List<KnownHostEntry> readKnownHostEntries(InputStream inStream, boolean okToClose) throws IOException {
- try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(inStream, okToClose), StandardCharsets.UTF_8)) {
- return readKnownHostEntries(reader, true);
- }
- }
-
- public static List<KnownHostEntry> readKnownHostEntries(Reader rdr, boolean okToClose) throws IOException {
- try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(rdr, okToClose))) {
- return readKnownHostEntries(buf);
- }
- }
-
- /**
- * Reads configuration entries
- *
- * @param rdr The {@link BufferedReader} to use
- * @return The {@link List} of read {@link KnownHostEntry}-ies
- * @throws IOException If failed to parse the read configuration
- */
- public static List<KnownHostEntry> readKnownHostEntries(BufferedReader rdr) throws IOException {
- List<KnownHostEntry> entries = null;
-
- int lineNumber = 1;
- for (String line = rdr.readLine(); line != null; line = rdr.readLine(), lineNumber++) {
- line = GenericUtils.trimToEmpty(line);
- if (GenericUtils.isEmpty(line)) {
- continue;
- }
-
- int pos = line.indexOf(SshConfigFileReader.COMMENT_CHAR);
- if (pos == 0) {
- continue;
- }
-
- if (pos > 0) {
- line = line.substring(0, pos);
- line = line.trim();
- }
-
- try {
- KnownHostEntry entry = parseKnownHostEntry(line);
- if (entry == null) {
- continue;
- }
-
- if (entries == null) {
- entries = new ArrayList<>();
- }
- entries.add(entry);
- } catch (RuntimeException | Error e) { // TODO consider consulting a user callback
- throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
- + " to parse line #" + lineNumber + " '" + line + "': " + e.getMessage());
- }
- }
-
- if (entries == null) {
- return Collections.emptyList();
- } else {
- return entries;
- }
- }
-
- public static KnownHostEntry parseKnownHostEntry(String line) {
- return parseKnownHostEntry(GenericUtils.isEmpty(line) ? null : new KnownHostEntry(), line);
- }
-
- public static <E extends KnownHostEntry> E parseKnownHostEntry(E entry, String data) {
- String line = GenericUtils.replaceWhitespaceAndTrim(data);
- if (GenericUtils.isEmpty(line) || (line.charAt(0) == PublicKeyEntry.COMMENT_CHAR)) {
- return entry;
- }
-
- entry.setConfigLine(line);
-
- if (line.charAt(0) == MARKER_INDICATOR) {
- int pos = line.indexOf(' ');
- ValidateUtils.checkTrue(pos > 0, "Missing marker name end delimiter in line=%s", data);
- ValidateUtils.checkTrue(pos > 1, "No marker name after indicator in line=%s", data);
- entry.setMarker(line.substring(1, pos));
- line = line.substring(pos + 1).trim();
- } else {
- entry.setMarker(null);
- }
-
- int pos = line.indexOf(' ');
- ValidateUtils.checkTrue(pos > 0, "Missing host patterns end delimiter in line=%s", data);
- String hostPattern = line.substring(0, pos);
- line = line.substring(pos + 1).trim();
-
- if (hostPattern.charAt(0) == KnownHostHashValue.HASHED_HOST_DELIMITER) {
- KnownHostHashValue hash =
- ValidateUtils.checkNotNull(KnownHostHashValue.parse(hostPattern),
- "Failed to extract host hash value from line=%s", data);
- entry.setHashedEntry(hash);
- entry.setPatterns(null);
- } else {
- entry.setHashedEntry(null);
- entry.setPatterns(parsePatterns(GenericUtils.split(hostPattern, ',')));
- }
-
- AuthorizedKeyEntry key =
- ValidateUtils.checkNotNull(AuthorizedKeyEntry.parseAuthorizedKeyEntry(line),
- "No valid key entry recovered from line=%s", data);
- entry.setKeyEntry(key);
- return entry;
- }
-}
[11/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PublicKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PublicKeyDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PublicKeyDecoder.java
deleted file mode 100644
index 793965c..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PublicKeyDecoder.java
+++ /dev/null
@@ -1,97 +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.util.security.eddsa;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.util.Collections;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.impl.AbstractPublicKeyEntryDecoder;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-import net.i2p.crypto.eddsa.EdDSAPrivateKey;
-import net.i2p.crypto.eddsa.EdDSAPublicKey;
-import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
-import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class Ed25519PublicKeyDecoder extends AbstractPublicKeyEntryDecoder<EdDSAPublicKey, EdDSAPrivateKey> {
- public static final Ed25519PublicKeyDecoder INSTANCE = new Ed25519PublicKeyDecoder();
-
- private Ed25519PublicKeyDecoder() {
- super(EdDSAPublicKey.class, EdDSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_ED25519)));
- }
-
- @Override
- public EdDSAPublicKey clonePublicKey(EdDSAPublicKey key) throws GeneralSecurityException {
- if (key == null) {
- return null;
- } else {
- return generatePublicKey(new EdDSAPublicKeySpec(key.getA(), key.getParams()));
- }
- }
-
- @Override
- public EdDSAPrivateKey clonePrivateKey(EdDSAPrivateKey key) throws GeneralSecurityException {
- if (key == null) {
- return null;
- } else {
- return generatePrivateKey(new EdDSAPrivateKeySpec(key.getSeed(), key.getParams()));
- }
- }
-
- @Override
- public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
- return SecurityUtils.getKeyPairGenerator(SecurityUtils.EDDSA);
- }
-
- @Override
- public String encodePublicKey(OutputStream s, EdDSAPublicKey key) throws IOException {
- Objects.requireNonNull(key, "No public key provided");
- KeyEntryResolver.encodeString(s, KeyPairProvider.SSH_ED25519);
- byte[] seed = getSeedValue(key);
- KeyEntryResolver.writeRLEBytes(s, seed);
- return KeyPairProvider.SSH_ED25519;
- }
-
- @Override
- public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
- return SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
- }
-
- @Override
- public EdDSAPublicKey decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException {
- byte[] seed = KeyEntryResolver.readRLEBytes(keyData);
- return EdDSAPublicKey.class.cast(SecurityUtils.generateEDDSAPublicKey(keyType, seed));
- }
-
- public static byte[] getSeedValue(EdDSAPublicKey key) {
- // a bit of reverse-engineering on the EdDSAPublicKeySpec
- return (key == null) ? null : key.getAbyte();
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java
deleted file mode 100644
index 61f16e9..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java
+++ /dev/null
@@ -1,104 +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.util.security.eddsa;
-
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.security.Provider;
-import java.security.Signature;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ReflectionUtils;
-import org.apache.sshd.common.util.security.AbstractSecurityProviderRegistrar;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class EdDSASecurityProviderRegistrar extends AbstractSecurityProviderRegistrar {
- public static final String PROVIDER_CLASS = "net.i2p.crypto.eddsa.EdDSASecurityProvider";
- // Do not define a static registrar instance to minimize class loading issues
- private final AtomicReference<Boolean> supportHolder = new AtomicReference<>(null);
-
- public EdDSASecurityProviderRegistrar() {
- super(SecurityUtils.EDDSA);
- }
-
- @Override
- public boolean isEnabled() {
- if (!super.isEnabled()) {
- return false;
- }
-
- // For backward compatibility
- return this.getBooleanProperty(SecurityUtils.EDDSA_SUPPORTED_PROP, true);
- }
-
- @Override
- public Provider getSecurityProvider() {
- try {
- return getOrCreateProvider(PROVIDER_CLASS);
- } catch (ReflectiveOperationException t) {
- Throwable e = GenericUtils.peelException(t);
- log.error("getSecurityProvider({}) failed ({}) to instantiate {}: {}",
- getName(), e.getClass().getSimpleName(), PROVIDER_CLASS, e.getMessage());
- if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- }
-
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public boolean isSecurityEntitySupported(Class<?> entityType, String name) {
- if (!isSupported()) {
- return false;
- }
-
- if (KeyPairGenerator.class.isAssignableFrom(entityType)
- || KeyFactory.class.isAssignableFrom(entityType)) {
- return Objects.compare(name, getName(), String.CASE_INSENSITIVE_ORDER) == 0;
- } else if (Signature.class.isAssignableFrom(entityType)) {
- return Objects.compare(SecurityUtils.CURVE_ED25519_SHA512, name, String.CASE_INSENSITIVE_ORDER) == 0;
- } else {
- return false;
- }
- }
-
- @Override
- public boolean isSupported() {
- Boolean supported;
- synchronized (supportHolder) {
- supported = supportHolder.get();
- if (supported != null) {
- return supported.booleanValue();
- }
-
- ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(getClass());
- supported = ReflectionUtils.isClassAvailable(cl, "net.i2p.crypto.eddsa.EdDSAKey");
- supportHolder.set(supported);
- }
-
- return supported.booleanValue();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java
deleted file mode 100644
index 242f550..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java
+++ /dev/null
@@ -1,201 +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.util.security.eddsa;
-
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Arrays;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-import net.i2p.crypto.eddsa.EdDSAEngine;
-import net.i2p.crypto.eddsa.EdDSAKey;
-import net.i2p.crypto.eddsa.EdDSAPrivateKey;
-import net.i2p.crypto.eddsa.EdDSAPublicKey;
-import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
-import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
-import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
-import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class EdDSASecurityProviderUtils {
- // See EdDSANamedCurveTable
- public static final String CURVE_ED25519_SHA512 = "Ed25519";
-
- private EdDSASecurityProviderUtils() {
- throw new UnsupportedOperationException("No instance");
- }
-
- public static Class<? extends PublicKey> getEDDSAPublicKeyType() {
- return EdDSAPublicKey.class;
- }
-
- public static Class<? extends PrivateKey> getEDDSAPrivateKeyType() {
- return EdDSAPrivateKey.class;
- }
-
- public static int getEDDSAKeySize(Key key) {
- return (SecurityUtils.isEDDSACurveSupported() && (key instanceof EdDSAKey)) ? 256 : -1;
- }
-
- public static boolean compareEDDSAPPublicKeys(PublicKey k1, PublicKey k2) {
- if (!SecurityUtils.isEDDSACurveSupported()) {
- return false;
- }
-
- if ((k1 instanceof EdDSAPublicKey) && (k2 instanceof EdDSAPublicKey)) {
- if (Objects.equals(k1, k2)) {
- return true;
- } else if (k1 == null || k2 == null) {
- return false; // both null is covered by Objects#equals
- }
-
- EdDSAPublicKey ed1 = (EdDSAPublicKey) k1;
- EdDSAPublicKey ed2 = (EdDSAPublicKey) k2;
- return Arrays.equals(ed1.getAbyte(), ed2.getAbyte())
- && compareEDDSAKeyParams(ed1.getParams(), ed2.getParams());
- }
-
- return false;
- }
-
- public static boolean isEDDSASignatureAlgorithm(String algorithm) {
- return EdDSAEngine.SIGNATURE_ALGORITHM.equalsIgnoreCase(algorithm);
- }
-
- public static EdDSAPublicKey recoverEDDSAPublicKey(PrivateKey key) throws GeneralSecurityException {
- ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
- if (!(key instanceof EdDSAPrivateKey)) {
- throw new InvalidKeyException("Private key is not " + SecurityUtils.EDDSA);
- }
-
- EdDSAPrivateKey prvKey = (EdDSAPrivateKey) key;
- EdDSAPublicKeySpec keySpec = new EdDSAPublicKeySpec(prvKey.getAbyte(), prvKey.getParams());
- KeyFactory factory = SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
- return EdDSAPublicKey.class.cast(factory.generatePublic(keySpec));
- }
-
- public static org.apache.sshd.common.signature.Signature getEDDSASignature() {
- ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
- return new SignatureEd25519();
- }
-
- public static boolean isEDDSAKeyFactoryAlgorithm(String algorithm) {
- return SecurityUtils.EDDSA.equalsIgnoreCase(algorithm);
- }
-
- public static boolean isEDDSAKeyPairGeneratorAlgorithm(String algorithm) {
- return SecurityUtils.EDDSA.equalsIgnoreCase(algorithm);
- }
-
- public static PublicKeyEntryDecoder<? extends PublicKey, ? extends PrivateKey> getEDDSAPublicKeyEntryDecoder() {
- ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
- return Ed25519PublicKeyDecoder.INSTANCE;
- }
-
- public static PrivateKeyEntryDecoder<? extends PublicKey, ? extends PrivateKey> getOpenSSHEDDSAPrivateKeyEntryDecoder() {
- ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
- return OpenSSHEd25519PrivateKeyEntryDecoder.INSTANCE;
- }
-
- public static boolean compareEDDSAPrivateKeys(PrivateKey k1, PrivateKey k2) {
- if (!SecurityUtils.isEDDSACurveSupported()) {
- return false;
- }
-
- if ((k1 instanceof EdDSAPrivateKey) && (k2 instanceof EdDSAPrivateKey)) {
- if (Objects.equals(k1, k2)) {
- return true;
- } else if (k1 == null || k2 == null) {
- return false; // both null is covered by Objects#equals
- }
-
- EdDSAPrivateKey ed1 = (EdDSAPrivateKey) k1;
- EdDSAPrivateKey ed2 = (EdDSAPrivateKey) k2;
- return Arrays.equals(ed1.getSeed(), ed2.getSeed())
- && compareEDDSAKeyParams(ed1.getParams(), ed2.getParams());
- }
-
- return false;
- }
-
- public static boolean compareEDDSAKeyParams(EdDSAParameterSpec s1, EdDSAParameterSpec s2) {
- if (Objects.equals(s1, s2)) {
- return true;
- } else if (s1 == null || s2 == null) {
- return false; // both null is covered by Objects#equals
- } else {
- return Objects.equals(s1.getHashAlgorithm(), s2.getHashAlgorithm())
- && Objects.equals(s1.getCurve(), s2.getCurve())
- && Objects.equals(s1.getB(), s2.getB());
- }
- }
-
- public static PublicKey generateEDDSAPublicKey(byte[] seed) throws GeneralSecurityException {
- if (!SecurityUtils.isEDDSACurveSupported()) {
- throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " not supported");
- }
-
- EdDSAParameterSpec params = EdDSANamedCurveTable.getByName(CURVE_ED25519_SHA512);
- EdDSAPublicKeySpec keySpec = new EdDSAPublicKeySpec(seed, params);
- KeyFactory factory = SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
- return factory.generatePublic(keySpec);
- }
-
- public static PrivateKey generateEDDSAPrivateKey(byte[] seed) throws GeneralSecurityException {
- if (!SecurityUtils.isEDDSACurveSupported()) {
- throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " not supported");
- }
-
- EdDSAParameterSpec params = EdDSANamedCurveTable.getByName(CURVE_ED25519_SHA512);
- EdDSAPrivateKeySpec keySpec = new EdDSAPrivateKeySpec(seed, params);
- KeyFactory factory = SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
- return factory.generatePrivate(keySpec);
- }
-
- public static <B extends Buffer> B putRawEDDSAPublicKey(B buffer, PublicKey key) {
- ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
- EdDSAPublicKey edKey = ValidateUtils.checkInstanceOf(key, EdDSAPublicKey.class, "Not an EDDSA public key: %s", key);
- byte[] seed = Ed25519PublicKeyDecoder.getSeedValue(edKey);
- ValidateUtils.checkNotNull(seed, "No seed extracted from key: %s", edKey.getA());
- buffer.putString(KeyPairProvider.SSH_ED25519);
- buffer.putBytes(seed);
- return buffer;
- }
-
- public static <B extends Buffer> B putEDDSAKeyPair(B buffer, PublicKey pubKey, PrivateKey prvKey) {
- ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
- ValidateUtils.checkInstanceOf(pubKey, EdDSAPublicKey.class, "Not an EDDSA public key: %s", pubKey);
- ValidateUtils.checkInstanceOf(prvKey, EdDSAPrivateKey.class, "Not an EDDSA private key: %s", prvKey);
- throw new UnsupportedOperationException("Full SSHD-440 implementation N/A");
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
deleted file mode 100644
index 4888818..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
+++ /dev/null
@@ -1,172 +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.util.security.eddsa;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Locale;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-import net.i2p.crypto.eddsa.EdDSAPrivateKey;
-import net.i2p.crypto.eddsa.EdDSAPublicKey;
-import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
-import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
-import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
-import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class OpenSSHEd25519PrivateKeyEntryDecoder extends AbstractPrivateKeyEntryDecoder<EdDSAPublicKey, EdDSAPrivateKey> {
- public static final OpenSSHEd25519PrivateKeyEntryDecoder INSTANCE = new OpenSSHEd25519PrivateKeyEntryDecoder();
- private static final int PK_SIZE = 32;
- private static final int SK_SIZE = 32;
- private static final int KEYPAIR_SIZE = PK_SIZE + SK_SIZE;
-
- public OpenSSHEd25519PrivateKeyEntryDecoder() {
- super(EdDSAPublicKey.class, EdDSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_ED25519)));
- }
-
- @Override
- public EdDSAPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
- throws IOException, GeneralSecurityException {
- if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
- throw new InvalidKeyException("Unsupported key type: " + keyType);
- }
-
- if (!SecurityUtils.isEDDSACurveSupported()) {
- throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " provider not supported");
- }
-
- // ed25519 bernstein naming: pk .. public key, sk .. secret key
- // we expect to find two byte arrays with the following structure (type:size):
- // [pk:32], [sk:32,pk:32]
-
- byte[] pk = KeyEntryResolver.readRLEBytes(keyData);
- byte[] keypair = KeyEntryResolver.readRLEBytes(keyData);
-
- if (pk.length != PK_SIZE) {
- throw new InvalidKeyException(String.format(Locale.ENGLISH, "Unexpected pk size: %s (expected %s)", pk.length, PK_SIZE));
- }
-
- if (keypair.length != KEYPAIR_SIZE) {
- throw new InvalidKeyException(String.format(Locale.ENGLISH, "Unexpected keypair size: %s (expected %s)", keypair.length, KEYPAIR_SIZE));
- }
-
- byte[] sk = Arrays.copyOf(keypair, SK_SIZE);
-
- // verify that the keypair contains the expected pk
- // yes, it's stored redundant, this seems to mimic the output structure of the keypair generation interface
- if (!Arrays.equals(pk, Arrays.copyOfRange(keypair, SK_SIZE, KEYPAIR_SIZE))) {
- throw new InvalidKeyException("Keypair did not contain the public key.");
- }
-
- // create the private key
- EdDSAParameterSpec params = EdDSANamedCurveTable.getByName(EdDSASecurityProviderUtils.CURVE_ED25519_SHA512);
- EdDSAPrivateKey privateKey = generatePrivateKey(new EdDSAPrivateKeySpec(sk, params));
-
- // the private key class contains the calculated public key (Abyte)
- // pointers to the corresponding code:
- // EdDSAPrivateKeySpec.EdDSAPrivateKeySpec(byte[], EdDSAParameterSpec): A = spec.getB().scalarMultiply(a);
- // EdDSAPrivateKey.EdDSAPrivateKey(EdDSAPrivateKeySpec): this.Abyte = this.A.toByteArray();
-
- // we can now verify the generated pk matches the one we read
- if (!Arrays.equals(privateKey.getAbyte(), pk)) {
- throw new InvalidKeyException("The provided pk does NOT match the computed pk for the given sk.");
- }
-
- return privateKey;
- }
-
- @Override
- public String encodePrivateKey(OutputStream s, EdDSAPrivateKey key) throws IOException {
- Objects.requireNonNull(key, "No private key provided");
-
- // ed25519 bernstein naming: pk .. public key, sk .. secret key
- // we are expected to write the following arrays (type:size):
- // [pk:32], [sk:32,pk:32]
-
- byte[] sk = key.getSeed();
- byte[] pk = key.getAbyte();
-
- Objects.requireNonNull(sk, "No seed");
-
- byte[] keypair = new byte[KEYPAIR_SIZE];
- System.arraycopy(sk, 0, keypair, 0, SK_SIZE);
- System.arraycopy(pk, 0, keypair, SK_SIZE, PK_SIZE);
-
- KeyEntryResolver.writeRLEBytes(s, pk);
- KeyEntryResolver.writeRLEBytes(s, keypair);
-
- return KeyPairProvider.SSH_ED25519;
- }
-
- @Override
- public boolean isPublicKeyRecoverySupported() {
- return true;
- }
-
- @Override
- public EdDSAPublicKey recoverPublicKey(EdDSAPrivateKey prvKey) throws GeneralSecurityException {
- return EdDSASecurityProviderUtils.recoverEDDSAPublicKey(prvKey);
- }
-
- @Override
- public EdDSAPublicKey clonePublicKey(EdDSAPublicKey key) throws GeneralSecurityException {
- if (key == null) {
- return null;
- } else {
- return generatePublicKey(new EdDSAPublicKeySpec(key.getA(), key.getParams()));
- }
- }
-
- @Override
- public EdDSAPrivateKey clonePrivateKey(EdDSAPrivateKey key) throws GeneralSecurityException {
- if (key == null) {
- return null;
- } else {
- return generatePrivateKey(new EdDSAPrivateKeySpec(key.getSeed(), key.getParams()));
- }
- }
-
- @Override
- public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
- return SecurityUtils.getKeyPairGenerator(SecurityUtils.EDDSA);
- }
-
- @Override
- public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
- return SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/SignatureEd25519.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/SignatureEd25519.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/SignatureEd25519.java
deleted file mode 100644
index 012be95..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/SignatureEd25519.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.util.security.eddsa;
-
-import java.util.Map;
-
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.signature.AbstractSignature;
-import org.apache.sshd.common.util.ValidateUtils;
-
-import net.i2p.crypto.eddsa.EdDSAEngine;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SignatureEd25519 extends AbstractSignature {
- public SignatureEd25519() {
- super(EdDSAEngine.SIGNATURE_ALGORITHM);
- }
-
- @Override
- public boolean verify(byte[] sig) throws Exception {
- byte[] data = sig;
- Map.Entry<String, byte[]> encoding = extractEncodedSignature(data);
- if (encoding != null) {
- String keyType = encoding.getKey();
- ValidateUtils.checkTrue(KeyPairProvider.SSH_ED25519.equals(keyType), "Mismatched key type: %s", keyType);
- data = encoding.getValue();
- }
-
- return doVerify(data);
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java b/sshd-core/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java
deleted file mode 100644
index 3b9beeb..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java
+++ /dev/null
@@ -1,28 +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.util.threads;
-
-import java.util.concurrent.ExecutorService;
-
-import org.apache.sshd.common.Closeable;
-
-public interface CloseableExecutorService extends ExecutorService, Closeable {
- // Nothing extra
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ExecutorServiceCarrier.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ExecutorServiceCarrier.java b/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ExecutorServiceCarrier.java
deleted file mode 100644
index b44bd46..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ExecutorServiceCarrier.java
+++ /dev/null
@@ -1,31 +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.util.threads;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface ExecutorServiceCarrier {
- /**
- * @return The {@link CloseableExecutorService} to use
- */
- CloseableExecutorService getExecutorService();
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/threads/NoCloseExecutor.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/NoCloseExecutor.java b/sshd-core/src/main/java/org/apache/sshd/common/util/threads/NoCloseExecutor.java
deleted file mode 100644
index cb42805..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/NoCloseExecutor.java
+++ /dev/null
@@ -1,160 +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.util.threads;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.DefaultCloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Wraps an {@link ExecutorService} as a {@link CloseableExecutorService}
- * and avoids calling its {@code shutdown} methods when the wrapper is shut down
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NoCloseExecutor implements CloseableExecutorService {
- protected final ExecutorService executor;
- protected final CloseFuture closeFuture;
-
- public NoCloseExecutor(ExecutorService executor) {
- this.executor = executor;
- closeFuture = new DefaultCloseFuture(null, null);
- }
-
- @Override
- public <T> Future<T> submit(Callable<T> task) {
- ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
- return executor.submit(task);
- }
-
- @Override
- public <T> Future<T> submit(Runnable task, T result) {
- ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
- return executor.submit(task, result);
- }
-
- @Override
- public Future<?> submit(Runnable task) {
- ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
- return executor.submit(task);
- }
-
- @Override
- public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
- throws InterruptedException {
- ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
- return executor.invokeAll(tasks);
- }
-
- @Override
- public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
- throws InterruptedException {
- ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
- return executor.invokeAll(tasks, timeout, unit);
- }
-
- @Override
- public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
- throws InterruptedException, ExecutionException {
- ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
- return executor.invokeAny(tasks);
- }
-
- @Override
- public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
- throws InterruptedException, ExecutionException, TimeoutException {
- ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
- return executor.invokeAny(tasks, timeout, unit);
- }
-
- @Override
- public void execute(Runnable command) {
- ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
- executor.execute(command);
- }
-
- @Override
- public void shutdown() {
- close(true);
- }
-
- @Override
- public List<Runnable> shutdownNow() {
- close(true);
- return Collections.emptyList();
- }
-
- @Override
- public boolean isShutdown() {
- return isClosed();
- }
-
- @Override
- public boolean isTerminated() {
- return isClosed();
- }
-
- @Override
- public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
- try {
- return closeFuture.await(timeout, unit);
- } catch (IOException e) {
- throw (InterruptedException) new InterruptedException().initCause(e);
- }
- }
-
- @Override
- public CloseFuture close(boolean immediately) {
- closeFuture.setClosed();
- return closeFuture;
- }
-
- @Override
- public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
- closeFuture.addListener(listener);
- }
-
- @Override
- public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
- closeFuture.removeListener(listener);
- }
-
- @Override
- public boolean isClosed() {
- return closeFuture.isClosed();
- }
-
- @Override
- public boolean isClosing() {
- return isClosed();
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/threads/SshThreadPoolExecutor.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/SshThreadPoolExecutor.java b/sshd-core/src/main/java/org/apache/sshd/common/util/threads/SshThreadPoolExecutor.java
deleted file mode 100644
index ccaa655..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/SshThreadPoolExecutor.java
+++ /dev/null
@@ -1,138 +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.util.threads;
-
-import java.util.List;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.RejectedExecutionHandler;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-import org.apache.sshd.common.util.closeable.AbstractCloseable;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SshThreadPoolExecutor extends ThreadPoolExecutor implements CloseableExecutorService {
- protected final DelegateCloseable closeable = new DelegateCloseable();
-
- protected class DelegateCloseable extends AbstractCloseable {
- protected DelegateCloseable() {
- super();
- }
-
- @Override
- protected CloseFuture doCloseGracefully() {
- shutdown();
- return closeFuture;
- }
-
- @Override
- protected void doCloseImmediately() {
- shutdownNow();
- super.doCloseImmediately();
- }
-
- protected void setClosed() {
- closeFuture.setClosed();
- }
- }
-
- public SshThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
- }
-
- public SshThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
- BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
- }
-
- public SshThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
- BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
- }
-
- public SshThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
- BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
- }
-
- @Override
- protected void terminated() {
- closeable.doCloseImmediately();
- }
-
- @Override
- public void shutdown() {
- super.shutdown();
- }
-
- @Override
- public List<Runnable> shutdownNow() {
- return super.shutdownNow();
- }
-
- @Override
- public boolean isShutdown() {
- return super.isShutdown();
- }
-
- @Override
- public boolean isTerminating() {
- return super.isTerminating();
- }
-
- @Override
- public boolean isTerminated() {
- return super.isTerminated();
- }
-
- @Override
- public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
- return super.awaitTermination(timeout, unit);
- }
-
- @Override
- public CloseFuture close(boolean immediately) {
- return closeable.close(immediately);
- }
-
- @Override
- public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
- closeable.addCloseFutureListener(listener);
- }
-
- @Override
- public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
- closeable.removeCloseFutureListener(listener);
- }
-
- @Override
- public boolean isClosed() {
- return closeable.isClosed();
- }
-
- @Override
- public boolean isClosing() {
- return closeable.isClosing();
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java
deleted file mode 100644
index 5dc0c7b..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java
+++ /dev/null
@@ -1,78 +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.util.threads;
-
-import java.security.AccessController;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * Default {@link ThreadFactory} used by {@link ThreadUtils} to create
- * thread pools if user did provide one
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SshdThreadFactory extends AbstractLoggingBean implements ThreadFactory {
- private final ThreadGroup group;
- private final AtomicInteger threadNumber = new AtomicInteger(1);
- private final String namePrefix;
-
- public SshdThreadFactory(String name) {
- SecurityManager s = System.getSecurityManager();
- group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
- String effectiveName = name.replace(' ', '-');
- namePrefix = "sshd-" + effectiveName + "-thread-";
- }
-
- @Override
- public Thread newThread(Runnable r) {
- Thread t;
- try {
- // see SSHD-668
- if (System.getSecurityManager() != null) {
- t = AccessController.doPrivileged((PrivilegedExceptionAction<Thread>) () ->
- new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0));
- } else {
- t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
- }
- } catch (PrivilegedActionException e) {
- Exception err = e.getException();
- if (err instanceof RuntimeException) {
- throw (RuntimeException) err;
- } else {
- throw new RuntimeException(err);
- }
- }
-
- if (!t.isDaemon()) {
- t.setDaemon(true);
- }
- if (t.getPriority() != Thread.NORM_PRIORITY) {
- t.setPriority(Thread.NORM_PRIORITY);
- }
- if (log.isTraceEnabled()) {
- log.trace("newThread({})[{}] runnable={}", group, t.getName(), r);
- }
- return t;
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java
deleted file mode 100644
index c803389..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java
+++ /dev/null
@@ -1,185 +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.util.threads;
-
-import java.util.LinkedHashSet;
-import java.util.Set;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Utility class for thread pools.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class ThreadUtils {
- private ThreadUtils() {
- throw new UnsupportedOperationException("No instance");
- }
-
- /**
- * Wraps an {@link CloseableExecutorService} in such a way as to "protect"
- * it for calls to the {@link CloseableExecutorService#shutdown()} or
- * {@link CloseableExecutorService#shutdownNow()}. All other calls are delegated as-is
- * to the original service. <B>Note:</B> the exposed wrapped proxy will
- * answer correctly the {@link CloseableExecutorService#isShutdown()} query if indeed
- * one of the {@code shutdown} methods was invoked.
- *
- * @param executorService The original service - ignored if {@code null}
- * @param shutdownOnExit If {@code true} then it is OK to shutdown the executor
- * so no wrapping takes place.
- * @return Either the original service or a wrapped one - depending on the
- * value of the <tt>shutdownOnExit</tt> parameter
- */
- public static CloseableExecutorService protectExecutorServiceShutdown(CloseableExecutorService executorService, boolean shutdownOnExit) {
- if (executorService == null || shutdownOnExit || executorService instanceof NoCloseExecutor) {
- return executorService;
- } else {
- return new NoCloseExecutor(executorService);
- }
- }
-
- public static CloseableExecutorService noClose(CloseableExecutorService executorService) {
- return protectExecutorServiceShutdown(executorService, false);
- }
-
- public static ClassLoader resolveDefaultClassLoader(Object anchor) {
- return resolveDefaultClassLoader(anchor == null ? null : anchor.getClass());
- }
-
- public static Iterable<ClassLoader> resolveDefaultClassLoaders(Object anchor) {
- return resolveDefaultClassLoaders(anchor == null ? null : anchor.getClass());
- }
-
- public static <T> T createDefaultInstance(Class<?> anchor, Class<T> targetType, String className)
- throws ReflectiveOperationException {
- return createDefaultInstance(resolveDefaultClassLoaders(anchor), targetType, className);
- }
-
- public static <T> T createDefaultInstance(ClassLoader cl, Class<T> targetType, String className)
- throws ReflectiveOperationException {
- Class<?> instanceType = cl.loadClass(className);
- Object instance = instanceType.newInstance();
- return targetType.cast(instance);
- }
-
- public static <T> T createDefaultInstance(Iterable<ClassLoader> cls, Class<T> targetType, String className)
- throws ReflectiveOperationException {
- for (ClassLoader cl : cls) {
- try {
- return createDefaultInstance(cl, targetType, className);
- } catch (ClassNotFoundException e) {
- // Ignore
- }
- }
- throw new ClassNotFoundException(className);
- }
-
- /**
- * <P>Attempts to find the most suitable {@link ClassLoader} as follows:</P>
- * <UL>
- * <LI><P>
- * Check the {@link Thread#getContextClassLoader()} value
- * </P></LI>
- *
- * <LI><P>
- * If no thread context class loader then check the anchor
- * class (if given) for its class loader
- * </P></LI>
- *
- * <LI><P>
- * If still no loader available, then use {@link ClassLoader#getSystemClassLoader()}
- * </P></LI>
- * </UL>
- *
- * @param anchor The anchor {@link Class} to use if no current thread
- * - ignored if {@code null}
- * context class loader
- * @return The resolver {@link ClassLoader}
- */
- public static ClassLoader resolveDefaultClassLoader(Class<?> anchor) {
- Thread thread = Thread.currentThread();
- ClassLoader cl = thread.getContextClassLoader();
- if (cl != null) {
- return cl;
- }
-
- if (anchor != null) {
- cl = anchor.getClassLoader();
- }
-
- if (cl == null) { // can happen for core Java classes
- cl = ClassLoader.getSystemClassLoader();
- }
-
- return cl;
- }
-
- public static Iterable<ClassLoader> resolveDefaultClassLoaders(Class<?> anchor) {
- Set<ClassLoader> cls = new LinkedHashSet<>();
- Thread thread = Thread.currentThread();
- ClassLoader cl = thread.getContextClassLoader();
- if (cl != null) {
- cls.add(cl);
- }
- if (anchor != null) {
- cls.add(anchor.getClassLoader());
- }
- cls.add(ClassLoader.getSystemClassLoader());
- return cls;
- }
-
- public static CloseableExecutorService newFixedThreadPoolIf(CloseableExecutorService executorService, String poolName, int nThreads) {
- return executorService == null ? newFixedThreadPool(poolName, nThreads) : executorService;
- }
-
- public static CloseableExecutorService newFixedThreadPool(String poolName, int nThreads) {
- return new SshThreadPoolExecutor(
- nThreads, nThreads,
- 0L, TimeUnit.MILLISECONDS, // TODO make this configurable
- new LinkedBlockingQueue<>(),
- new SshdThreadFactory(poolName),
- new ThreadPoolExecutor.CallerRunsPolicy());
- }
-
- public static CloseableExecutorService newCachedThreadPoolIf(CloseableExecutorService executorService, String poolName) {
- return executorService == null ? newCachedThreadPool(poolName) : executorService;
- }
-
- public static CloseableExecutorService newCachedThreadPool(String poolName) {
- return new SshThreadPoolExecutor(
- 0, Integer.MAX_VALUE, // TODO make this configurable
- 60L, TimeUnit.SECONDS, // TODO make this configurable
- new SynchronousQueue<>(),
- new SshdThreadFactory(poolName),
- new ThreadPoolExecutor.CallerRunsPolicy());
- }
-
- public static ScheduledExecutorService newSingleThreadScheduledExecutor(String poolName) {
- return new ScheduledThreadPoolExecutor(1, new SshdThreadFactory(poolName));
- }
-
- public static CloseableExecutorService newSingleThreadExecutor(String poolName) {
- return newFixedThreadPool(poolName, 1);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/PublickeyAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/PublickeyAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/PublickeyAuthenticator.java
index 2d7a908..a498478 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/PublickeyAuthenticator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/PublickeyAuthenticator.java
@@ -18,8 +18,14 @@
*/
package org.apache.sshd.server.auth.pubkey;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
import java.security.PublicKey;
+import java.util.Collection;
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.server.auth.AsyncAuthException;
import org.apache.sshd.server.session.ServerSession;
@@ -42,4 +48,15 @@ public interface PublickeyAuthenticator {
* @throws AsyncAuthException If the authentication is performed asynchronously
*/
boolean authenticate(String username, PublicKey key, ServerSession session) throws AsyncAuthException;
+
+ static PublickeyAuthenticator fromAuthorizedEntries(
+ PublicKeyEntryResolver fallbackResolver, Collection<? extends AuthorizedKeyEntry> entries)
+ throws IOException, GeneralSecurityException {
+ Collection<PublicKey> keys = AuthorizedKeyEntry.resolveAuthorizedKeys(fallbackResolver, entries);
+ if (GenericUtils.isEmpty(keys)) {
+ return RejectAllPublickeyAuthenticator.INSTANCE;
+ } else {
+ return new KeySetPublickeyAuthenticator(keys);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java b/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java
index 381d2a3..bf4a958 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java
@@ -22,6 +22,7 @@ import java.nio.file.Paths;
import org.apache.sshd.common.PropertyResolver;
import org.apache.sshd.common.PropertyResolverUtils;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
import org.apache.sshd.common.config.SshConfigFileReader;
import org.apache.sshd.common.helpers.AbstractFactoryManager;
import org.apache.sshd.common.util.GenericUtils;
@@ -62,7 +63,8 @@ public final class SshServerConfigFileReader {
throw new UnsupportedOperationException("No instance allowed");
}
- public static <S extends SshServer> S configure(S server, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
+ public static <S extends SshServer> S configure(
+ S server, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
SshConfigFileReader.configure((AbstractFactoryManager) server, props, lenient, ignoreUnsupported);
SshConfigFileReader.configureKeyExchanges(server, props, lenient, ServerBuilder.DH2KEX, ignoreUnsupported);
return server;
@@ -80,27 +82,31 @@ public final class SshServerConfigFileReader {
}
public static AgentForwardingFilter resolveAgentForwardingFilter(PropertyResolver options) {
- String value = PropertyResolverUtils.getStringProperty(options, ALLOW_AGENT_FORWARDING_CONFIG_PROP, DEFAULT_AGENT_FORWARDING);
- return AgentForwardingFilter.of(SshConfigFileReader.parseBooleanValue(value));
+ String value = PropertyResolverUtils.getStringProperty(options,
+ ALLOW_AGENT_FORWARDING_CONFIG_PROP, DEFAULT_AGENT_FORWARDING);
+ return AgentForwardingFilter.of(ConfigFileReaderSupport.parseBooleanValue(value));
}
public static TcpForwardingFilter resolveTcpForwardingFilter(PropertyResolver options) {
- String value = PropertyResolverUtils.getStringProperty(options, ALLOW_TCP_FORWARDING_CONFIG_PROP, DEFAULT_TCP_FORWARDING);
+ String value = PropertyResolverUtils.getStringProperty(options,
+ ALLOW_TCP_FORWARDING_CONFIG_PROP, DEFAULT_TCP_FORWARDING);
TcpForwardingFilter filter = AllowTcpForwardingValue.fromString(value);
ValidateUtils.checkNotNull(filter, "Unknown %s value: %s", ALLOW_TCP_FORWARDING_CONFIG_PROP, value);
return filter;
}
public static X11ForwardingFilter resolveX11ForwardingFilter(PropertyResolver options) {
- String value = PropertyResolverUtils.getStringProperty(options, ALLOW_X11_FORWARDING_CONFIG_PROP, DEFAULT_X11_FORWARDING);
- return X11ForwardingFilter.of(SshConfigFileReader.parseBooleanValue(value));
+ String value = PropertyResolverUtils.getStringProperty(options,
+ ALLOW_X11_FORWARDING_CONFIG_PROP, DEFAULT_X11_FORWARDING);
+ return X11ForwardingFilter.of(ConfigFileReaderSupport.parseBooleanValue(value));
}
public static Object resolveBanner(PropertyResolver options) {
String bannerOption = PropertyResolverUtils.getString(options, BANNER_CONFIG_PROP);
if (GenericUtils.isEmpty(bannerOption)) {
- bannerOption = PropertyResolverUtils.getStringProperty(options, VISUAL_HOST_KEY, DEFAULT_VISUAL_HOST_KEY);
- if (SshConfigFileReader.parseBooleanValue(bannerOption)) {
+ bannerOption = PropertyResolverUtils.getStringProperty(options,
+ VISUAL_HOST_KEY, DEFAULT_VISUAL_HOST_KEY);
+ if (ConfigFileReaderSupport.parseBooleanValue(bannerOption)) {
bannerOption = ServerAuthenticationManager.AUTO_WELCOME_BANNER_VALUE;
} else {
bannerOption = null;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeysAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeysAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeysAuthenticator.java
index cfc7ba1..b05837b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeysAuthenticator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/AuthorizedKeysAuthenticator.java
@@ -131,7 +131,9 @@ public class AuthorizedKeysAuthenticator extends ModifiableFileWatcher implement
if (exists()) {
Collection<AuthorizedKeyEntry> entries = reloadAuthorizedKeys(path, username, session);
if (GenericUtils.size(entries) > 0) {
- delegateHolder.set(AuthorizedKeyEntry.fromAuthorizedEntries(getFallbackPublicKeyEntryResolver(), entries));
+ PublickeyAuthenticator authDelegate =
+ PublickeyAuthenticator.fromAuthorizedEntries(getFallbackPublicKeyEntryResolver(), entries);
+ delegateHolder.set(authDelegate);
}
} else {
log.info("resolvePublickeyAuthenticator(" + username + ")[" + session + "] no authorized keys file at " + path);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java b/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
deleted file mode 100644
index 9131f99..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
+++ /dev/null
@@ -1,293 +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.server.keyprovider;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.PublicKey;
-import java.security.spec.AlgorithmParameterSpec;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.BuiltinIdentities;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Holds a <U>single</U> {@link KeyPair} which is generated the 1st time
- * {@link #loadKeys()} is called. If there is a file backing it up and the
- * file exists, the key is loaded from it. Otherwise a new key pair is
- * generated and saved (provided a path is configured and {@link #isOverwriteAllowed()}
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairProvider {
- public static final String DEFAULT_ALGORITHM = KeyUtils.RSA_ALGORITHM;
- public static final boolean DEFAULT_ALLOWED_TO_OVERWRITE = true;
-
- private final AtomicReference<KeyPair> keyPairHolder = new AtomicReference<>();
-
- private Path path;
- private String algorithm = DEFAULT_ALGORITHM;
- private int keySize;
- private AlgorithmParameterSpec keySpec;
- private boolean overwriteAllowed = DEFAULT_ALLOWED_TO_OVERWRITE;
-
- protected AbstractGeneratorHostKeyProvider() {
- super();
- }
-
- public Path getPath() {
- return path;
- }
-
- public void setFile(File file) {
- setPath((file == null) ? null : file.toPath());
- }
-
- public void setPath(Path path) {
- this.path = (path == null) ? null : path.toAbsolutePath();
- }
-
- public String getAlgorithm() {
- return algorithm;
- }
-
- public void setAlgorithm(String algorithm) {
- this.algorithm = algorithm;
- }
-
- public int getKeySize() {
- return keySize;
- }
-
- public void setKeySize(int keySize) {
- this.keySize = keySize;
- }
-
- public AlgorithmParameterSpec getKeySpec() {
- return keySpec;
- }
-
- public void setKeySpec(AlgorithmParameterSpec keySpec) {
- this.keySpec = keySpec;
- }
-
- public boolean isOverwriteAllowed() {
- return overwriteAllowed;
- }
-
- public void setOverwriteAllowed(boolean overwriteAllowed) {
- this.overwriteAllowed = overwriteAllowed;
- }
-
- public void clearLoadedKeys() {
- KeyPair kp;
- synchronized (keyPairHolder) {
- kp = keyPairHolder.getAndSet(null);
- }
-
- if ((kp != null) & log.isDebugEnabled()) {
- PublicKey key = kp.getPublic();
- log.debug("clearLoadedKeys({}) removed key={}-{}",
- getPath(), KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
- }
- }
-
- @Override // co-variant return
- public synchronized List<KeyPair> loadKeys() {
- Path keyPath = getPath();
- KeyPair kp;
- synchronized (keyPairHolder) {
- kp = keyPairHolder.get();
- if (kp == null) {
- try {
- kp = resolveKeyPair(keyPath);
- if (kp != null) {
- keyPairHolder.set(kp);
- }
- } catch (Throwable t) {
- log.warn("loadKeys({}) Failed ({}) to resolve: {}",
- keyPath, t.getClass().getSimpleName(), t.getMessage());
- if (log.isDebugEnabled()) {
- log.debug("loadKeys(" + keyPath + ") resolution failure details", t);
- }
- }
- }
- }
-
- if (kp == null) {
- return Collections.emptyList();
- } else {
- return Collections.singletonList(kp);
- }
- }
-
- protected KeyPair resolveKeyPair(Path keyPath) throws IOException, GeneralSecurityException {
- String alg = getAlgorithm();
- KeyPair kp;
- if (keyPath != null) {
- try {
- kp = loadFromFile(alg, keyPath);
- if (kp != null) {
- return kp;
- }
- } catch (Throwable e) {
- log.warn("resolveKeyPair({}) Failed ({}) to load: {}",
- keyPath, e.getClass().getSimpleName(), e.getMessage());
- if (log.isDebugEnabled()) {
- log.debug("resolveKeyPair(" + keyPath + ") load failure details", e);
- }
- }
- }
-
- // either no file specified or no key in file
- try {
- kp = generateKeyPair(alg);
- if (kp == null) {
- return null;
- }
-
- if (log.isDebugEnabled()) {
- PublicKey key = kp.getPublic();
- log.debug("resolveKeyPair({}) generated {} key={}-{}",
- keyPath, alg, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
- }
- } catch (Throwable e) {
- log.warn("resolveKeyPair({})[{}] Failed ({}) to generate {} key-pair: {}",
- keyPath, alg, e.getClass().getSimpleName(), alg, e.getMessage());
- if (log.isDebugEnabled()) {
- log.debug("resolveKeyPair(" + keyPath + ")[" + alg + "] key-pair generation failure details", e);
- }
-
- return null;
- }
-
- if (keyPath != null) {
- try {
- writeKeyPair(kp, keyPath);
- } catch (Throwable e) {
- log.warn("resolveKeyPair({})[{}] Failed ({}) to write {} key: {}",
- alg, keyPath, e.getClass().getSimpleName(), alg, e.getMessage());
- if (log.isDebugEnabled()) {
- log.debug("resolveKeyPair(" + keyPath + ")[" + alg + "] write failure details", e);
- }
- }
- }
-
- return kp;
- }
-
- protected KeyPair loadFromFile(String alg, Path keyPath) throws IOException, GeneralSecurityException {
- LinkOption[] options = IoUtils.getLinkOptions(true);
- if ((!Files.exists(keyPath, options)) || (!Files.isRegularFile(keyPath, options))) {
- return null;
- }
-
- KeyPair kp = readKeyPair(keyPath, IoUtils.EMPTY_OPEN_OPTIONS);
- if (kp == null) {
- return null;
- }
-
- PublicKey key = kp.getPublic();
- String keyAlgorithm = key.getAlgorithm();
- if (BuiltinIdentities.Constants.ECDSA.equalsIgnoreCase(keyAlgorithm)) {
- keyAlgorithm = KeyUtils.EC_ALGORITHM;
- } else if (BuiltinIdentities.Constants.ED25519.equalsIgnoreCase(keyAlgorithm)) {
- keyAlgorithm = SecurityUtils.EDDSA;
- }
-
- if (Objects.equals(alg, keyAlgorithm)) {
- if (log.isDebugEnabled()) {
- log.debug("resolveKeyPair({}) loaded key={}-{}",
- keyPath, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
- }
- return kp;
- }
-
- // Not same algorithm - start again
- if (log.isDebugEnabled()) {
- log.debug("resolveKeyPair({}) mismatched loaded key algorithm: expected={}, loaded={}",
- keyPath, alg, keyAlgorithm);
- }
- Files.deleteIfExists(keyPath);
- return null;
- }
-
- protected KeyPair readKeyPair(Path keyPath, OpenOption... options) throws IOException, GeneralSecurityException {
- try (InputStream inputStream = Files.newInputStream(keyPath, options)) {
- return doReadKeyPair(keyPath.toString(), inputStream);
- }
- }
-
- protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException {
- return SecurityUtils.loadKeyPairIdentity(resourceKey, inputStream, null);
- }
-
- protected void writeKeyPair(KeyPair kp, Path keyPath, OpenOption... options) throws IOException, GeneralSecurityException {
- if ((!Files.exists(keyPath)) || isOverwriteAllowed()) {
- try (OutputStream os = Files.newOutputStream(keyPath, options)) {
- doWriteKeyPair(keyPath.toString(), kp, os);
- } catch (Throwable e) {
- log.warn("writeKeyPair({}) failed ({}) to write key {}: {}",
- keyPath, e.getClass().getSimpleName(), e.getMessage());
- if (log.isDebugEnabled()) {
- log.debug("writeKeyPair(" + keyPath + ") write failure details", e);
- }
- }
- } else {
- log.error("Overwriting key ({}) is disabled: using throwaway {}: {}",
- keyPath, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint((kp == null) ? null : kp.getPublic()));
- }
- }
-
- protected abstract void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException;
-
- protected KeyPair generateKeyPair(String algorithm) throws GeneralSecurityException {
- KeyPairGenerator generator = SecurityUtils.getKeyPairGenerator(algorithm);
- if (keySpec != null) {
- generator.initialize(keySpec);
- log.info("generateKeyPair(" + algorithm + ") generating host key - spec=" + keySpec.getClass().getSimpleName());
- } else if (keySize != 0) {
- generator.initialize(keySize);
- log.info("generateKeyPair(" + algorithm + ") generating host key - size=" + keySize);
- } else if (KeyUtils.EC_ALGORITHM.equals(algorithm)) {
- // If left to our own devices choose the biggest key size possible
- int numCurves = ECCurves.SORTED_KEY_SIZE.size();
- ECCurves curve = ECCurves.SORTED_KEY_SIZE.get(numCurves - 1);
- generator.initialize(curve.getParameters());
- }
-
- return generator.generateKeyPair();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java b/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
deleted file mode 100644
index 3bccde8..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
+++ /dev/null
@@ -1,67 +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.server.keyprovider;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.spec.InvalidKeySpecException;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SimpleGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProvider {
- public SimpleGeneratorHostKeyProvider() {
- super();
- }
-
- public SimpleGeneratorHostKeyProvider(File file) {
- this((file == null) ? null : file.toPath());
- }
-
- public SimpleGeneratorHostKeyProvider(Path path) {
- setPath(path);
- }
-
- @Override
- protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException {
- try (ObjectInputStream r = new ObjectInputStream(inputStream)) {
- try {
- return (KeyPair) r.readObject();
- } catch (ClassNotFoundException e) {
- throw new InvalidKeySpecException("Missing classes: " + e.getMessage(), e);
- }
- }
- }
-
- @Override
- protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
- try (ObjectOutputStream w = new ObjectOutputStream(outputStream)) {
- w.writeObject(kp);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java b/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java
index 774620b..906df28 100644
--- a/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/KeepAliveTest.java
@@ -35,9 +35,9 @@ import org.apache.sshd.common.channel.Channel;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.command.Command;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.apache.sshd.util.test.EchoShell;
import org.apache.sshd.util.test.EchoShellFactory;
-import org.apache.sshd.util.test.Utils;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
@@ -65,12 +65,12 @@ public class KeepAliveTest extends BaseTestSupport {
@BeforeClass
public static void setupClientAndServer() throws Exception {
- sshd = Utils.setupTestServer(KeepAliveTest.class);
+ sshd = CoreTestSupportUtils.setupTestServer(KeepAliveTest.class);
sshd.setShellFactory(new TestEchoShellFactory());
sshd.start();
port = sshd.getPort();
- client = Utils.setupTestClient(KeepAliveTest.class);
+ client = CoreTestSupportUtils.setupTestClient(KeepAliveTest.class);
client.start();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java b/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
index 7deebfc..dffb00b 100644
--- a/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
@@ -43,9 +43,9 @@ import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.command.Command;
import org.apache.sshd.server.forward.AcceptAllForwardingFilter;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.apache.sshd.util.test.EchoShell;
import org.apache.sshd.util.test.EchoShellFactory;
-import org.apache.sshd.util.test.Utils;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
@@ -109,7 +109,7 @@ public class AgentTest extends BaseTestSupport {
ProxyAgentFactory agentFactory = new ProxyAgentFactory();
LocalAgentFactory localAgentFactory = new LocalAgentFactory();
String username = getCurrentTestName();
- KeyPair pair = Utils.createTestKeyPairProvider("dsaprivkey.pem").loadKey(KeyPairProvider.SSH_DSS);
+ KeyPair pair = CommonTestSupportUtils.createTestKeyPairProvider("dsaprivkey.pem").loadKey(KeyPairProvider.SSH_DSS);
localAgentFactory.getAgent().addIdentity(pair, username);
try (SshServer sshd1 = setupTestServer()) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java b/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java
index 9989dd1..e5e928a 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java
@@ -40,7 +40,7 @@ import org.apache.sshd.common.session.SessionListener;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
@@ -62,11 +62,11 @@ public class ClientSessionListenerTest extends BaseTestSupport {
@BeforeClass
public static void setupClientAndServer() throws Exception {
- sshd = Utils.setupTestServer(ClientSessionListenerTest.class);
+ sshd = CoreTestSupportUtils.setupTestServer(ClientSessionListenerTest.class);
sshd.start();
port = sshd.getPort();
- client = Utils.setupTestClient(ClientSessionListenerTest.class);
+ client = CoreTestSupportUtils.setupTestClient(ClientSessionListenerTest.class);
client.start();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/auth/PasswordIdentityProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/auth/PasswordIdentityProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/client/auth/PasswordIdentityProviderTest.java
deleted file mode 100644
index 4eaeed6..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/client/auth/PasswordIdentityProviderTest.java
+++ /dev/null
@@ -1,73 +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.client.auth;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Date;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class PasswordIdentityProviderTest extends BaseTestSupport {
- public PasswordIdentityProviderTest() {
- super();
- }
-
- @Test
- public void testMultiProvider() {
- String[][] values = {
- {getClass().getSimpleName(), getCurrentTestName()},
- {new Date(System.currentTimeMillis()).toString()},
- {getClass().getPackage().getName()}
- };
- List<String> expected = new ArrayList<>();
- Collection<PasswordIdentityProvider> providers = new LinkedList<>();
- for (String[] va : values) {
- Collection<String> passwords = Arrays.asList(va);
- expected.addAll(passwords);
-
- PasswordIdentityProvider p = PasswordIdentityProvider.wrapPasswords(passwords);
- assertProviderContents("Wrapped", p, passwords);
- providers.add(p);
- }
-
- PasswordIdentityProvider p = PasswordIdentityProvider.multiProvider(providers);
- assertProviderContents("Multi", p, expected);
- }
-
- private static void assertProviderContents(String message, PasswordIdentityProvider p, Iterable<String> expected) {
- assertNotNull(message + ": no provider", p);
- assertEquals(message, expected, p.loadPasswords());
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/channel/ChannelExecTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/channel/ChannelExecTest.java b/sshd-core/src/test/java/org/apache/sshd/client/channel/ChannelExecTest.java
index 038ce55..6bc5213 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/channel/ChannelExecTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/channel/ChannelExecTest.java
@@ -28,7 +28,7 @@ import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.util.test.BaseTestSupport;
import org.apache.sshd.util.test.CommandExecutionHelper;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
@@ -50,7 +50,7 @@ public class ChannelExecTest extends BaseTestSupport {
@BeforeClass
public static void setupClientAndServer() throws Exception {
- sshd = Utils.setupTestServer(ChannelExecTest.class);
+ sshd = CoreTestSupportUtils.setupTestServer(ChannelExecTest.class);
sshd.setCommandFactory(command -> new CommandExecutionHelper(command) {
@Override
protected boolean handleCommandLine(String command) throws Exception {
@@ -63,7 +63,7 @@ public class ChannelExecTest extends BaseTestSupport {
sshd.start();
port = sshd.getPort();
- client = Utils.setupTestClient(ChannelExecTest.class);
+ client = CoreTestSupportUtils.setupTestClient(ChannelExecTest.class);
client.start();
}
[30/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/IoUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/IoUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/IoUtilsTest.java
new file mode 100644
index 0000000..a94811d
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/IoUtilsTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.nio.file.LinkOption;
+
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class IoUtilsTest extends JUnitTestSupport {
+ public IoUtilsTest() {
+ super();
+ }
+
+ @Test
+ public void testFollowLinks() {
+ assertTrue("Null ?", IoUtils.followLinks((LinkOption[]) null));
+ assertTrue("Empty ?", IoUtils.followLinks(IoUtils.EMPTY_LINK_OPTIONS));
+ assertFalse("No-follow ?", IoUtils.followLinks(IoUtils.getLinkOptions(false)));
+ }
+
+ @Test
+ public void testGetEOLBytes() {
+ byte[] expected = IoUtils.getEOLBytes();
+ assertTrue("Empty bytes", NumberUtils.length(expected) > 0);
+
+ for (int index = 1; index < Byte.SIZE; index++) {
+ byte[] actual = IoUtils.getEOLBytes();
+ assertNotSame("Same bytes received at iteration " + index, expected, actual);
+ assertArrayEquals("Mismatched bytes at iteration " + index, expected, actual);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java
new file mode 100644
index 0000000..e009527
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class LimitInputStreamTest extends JUnitTestSupport {
+ public LimitInputStreamTest() {
+ super();
+ }
+
+ @Test
+ public void testReadLimit() throws IOException {
+ Path targetPath = detectTargetFolder();
+ Path rootFolder = assertHierarchyTargetFolderExists(targetPath.resolve(getClass().getSimpleName()));
+ Path inputFile = rootFolder.resolve(getCurrentTestName() + ".bin");
+ byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
+ Files.write(inputFile, data);
+
+ try (InputStream in = Files.newInputStream(inputFile)) {
+ int maxLen = data.length / 2;
+ byte[] expected = new byte[maxLen];
+ System.arraycopy(data, 0, expected, 0, expected.length);
+
+ byte[] actual = new byte[expected.length];
+ try (LimitInputStream limited = new LimitInputStream(in, expected.length)) {
+ assertTrue("Limited stream not marked as open", limited.isOpen());
+ assertEquals("Mismatched initial available data size", expected.length, limited.available());
+
+ int readLen = limited.read(actual);
+ assertEquals("Incomplete actual data read", actual.length, readLen);
+ assertArrayEquals("Mismatched read data", expected, actual);
+ assertEquals("Mismatched remaining available data size", 0, limited.available());
+
+ readLen = limited.read();
+ assertTrue("Unexpected success to read one more byte: " + readLen, readLen < 0);
+
+ readLen = limited.read(actual);
+ assertTrue("Unexpected success to read extra buffer: " + readLen, readLen < 0);
+
+ limited.close();
+ assertFalse("Limited stream still marked as open", limited.isOpen());
+
+ try {
+ readLen = limited.read();
+ fail("Unexpected one byte read success after close");
+ } catch (IOException e) {
+ // expected
+ }
+
+ try {
+ readLen = limited.read(actual);
+ fail("Unexpected buffer read success after close: " + readLen);
+ } catch (IOException e) {
+ // expected
+ }
+
+ try {
+ readLen = limited.read(actual);
+ fail("Unexpected buffer read success after close: " + readLen);
+ } catch (IOException e) {
+ // expected
+ }
+
+ try {
+ readLen = (int) limited.skip(Byte.SIZE);
+ fail("Unexpected skip success after close: " + readLen);
+ } catch (IOException e) {
+ // expected
+ }
+
+ try {
+ readLen = limited.available();
+ fail("Unexpected available success after close: " + readLen);
+ } catch (IOException e) {
+ // expected
+ }
+ }
+
+ // make sure underlying stream not closed
+ int readLen = in.read(actual);
+ assertEquals("Incomplete extra data read", Math.min(actual.length, data.length - expected.length), readLen);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/ModifiableFileWatcherTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/ModifiableFileWatcherTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/ModifiableFileWatcherTest.java
new file mode 100644
index 0000000..05343af
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/ModifiableFileWatcherTest.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.OsUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class ModifiableFileWatcherTest extends JUnitTestSupport {
+ public ModifiableFileWatcherTest() {
+ super();
+ }
+
+ @Test // see SSHD-606
+ public void testValidateStrictConfigFilePermissions() throws IOException {
+ Path file = getTempTargetRelativeFile(getClass().getSimpleName(), getCurrentTestName());
+ outputDebugMessage("%s deletion result=%s", file, Files.deleteIfExists(file));
+ assertNull("Unexpected violation for non-existent file: " + file, ModifiableFileWatcher.validateStrictConfigFilePermissions(file));
+
+ assertHierarchyTargetFolderExists(file.getParent());
+ try (OutputStream output = Files.newOutputStream(file)) {
+ output.write((getClass().getName() + "#" + getCurrentTestName() + "@" + new Date(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
+ }
+
+ Collection<PosixFilePermission> perms = IoUtils.getPermissions(file);
+ if (GenericUtils.isEmpty(perms)) {
+ assertNull("Unexpected violation for no permissions file: " + file, ModifiableFileWatcher.validateStrictConfigFilePermissions(file));
+ } else if (OsUtils.isUNIX()) {
+ Map.Entry<String, Object> violation = null;
+ for (PosixFilePermission p : ModifiableFileWatcher.STRICTLY_PROHIBITED_FILE_PERMISSION) {
+ if (perms.contains(p)) {
+ violation = ModifiableFileWatcher.validateStrictConfigFilePermissions(file);
+ assertNotNull("Unexpected success for permission=" + p + " of file " + file + " permissions=" + perms, violation);
+ break;
+ }
+ }
+
+ if (violation == null) { // we do not expected a failure if no permissions have been violated
+ assertNull("Unexpected UNIX violation for file " + file + " permissions=" + perms, ModifiableFileWatcher.validateStrictConfigFilePermissions(file));
+ }
+ } else {
+ assertNull("Unexpected Windows violation for file " + file + " permissions=" + perms, ModifiableFileWatcher.validateStrictConfigFilePermissions(file));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java
new file mode 100644
index 0000000..564dc42
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.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.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Date;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class NoCloseInputStreamTest extends JUnitTestSupport {
+ public NoCloseInputStreamTest() {
+ super();
+ }
+
+ @Test
+ public void testCanKeepReadingAfterClose() throws IOException {
+ byte[] expected = (getClass().getName() + "#" + getCurrentTestName() + "@" + new Date()).getBytes(StandardCharsets.UTF_8);
+ Path dir = createTempClassFolder();
+ Path file = Files.write(dir.resolve(getCurrentTestName() + ".txt"), expected);
+ try (InputStream fileStream = Files.newInputStream(file);
+ InputStream shielded = new NoCloseInputStream(fileStream)) {
+ int index = 0;
+
+ for (; index < (expected.length / 2); index++) {
+ shielded.close();
+
+ int readValue = shielded.read();
+ if (readValue == -1) {
+ fail("Premature EOF after shield read of " + index + " bytes");
+ }
+
+ byte expValue = expected[index];
+ byte actValue = (byte) (readValue & 0xFF);
+ if (expValue != actValue) {
+ fail("Mismatched shielded read value after " + index + " bytes");
+ }
+ }
+
+ for (; index < expected.length; index++) {
+ int readValue = fileStream.read();
+ if (readValue == -1) {
+ fail("Premature EOF after original read of " + index + " bytes");
+ }
+ byte expValue = expected[index];
+ byte actValue = (byte) (readValue & 0xFF);
+ if (expValue != actValue) {
+ fail("Mismatched original read value after " + index + " bytes");
+ }
+ }
+
+ int readValue = shielded.read();
+ assertEquals("Shielded EOF not signalled", -1, readValue);
+
+ readValue = fileStream.read();
+ assertEquals("Original EOF not signalled", -1, readValue);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java
new file mode 100644
index 0000000..f01f132
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Date;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class NoCloseOutputStreamTest extends JUnitTestSupport {
+ public NoCloseOutputStreamTest() {
+ super();
+ }
+
+ @Test
+ public void testCanKeepWritingAfterClose() throws IOException {
+ Path dir = createTempClassFolder();
+ Path file = dir.resolve(getCurrentTestName() + ".txt");
+ Files.deleteIfExists(file);
+
+ String expectedOutput = getClass().getName() + "#" + getCurrentTestName() + "@" + new Date();
+ byte[] expected = expectedOutput.getBytes(StandardCharsets.UTF_8);
+ try (OutputStream fileStream = Files.newOutputStream(file);
+ OutputStream shielded = new NoCloseOutputStream(fileStream)) {
+ int index = 0;
+ for (; index < (expected.length / 2); index++) {
+ shielded.close();
+ shielded.write(expected[index] & 0xFF);
+ }
+
+ fileStream.write(expected, index, expected.length - index);
+ }
+
+ byte[] actual = Files.readAllBytes(file);
+ String actualOutput = new String(actual, StandardCharsets.UTF_8);
+ assertEquals(expectedOutput, actualOutput);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java
new file mode 100644
index 0000000..14a72fc
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Date;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class NoCloseReaderTest extends JUnitTestSupport {
+ public NoCloseReaderTest() {
+ super();
+ }
+
+ @Test
+ public void testCanKeepReadingAfterClose() throws IOException {
+ String expected = getClass().getName() + "#" + getCurrentTestName() + "@" + new Date();
+ Path dir = createTempClassFolder();
+ Path file = Files.write(dir.resolve(getCurrentTestName() + ".txt"), expected.getBytes(StandardCharsets.UTF_8));
+ try (InputStream fileStream = Files.newInputStream(file);
+ Reader rdr = new InputStreamReader(fileStream, StandardCharsets.UTF_8);
+ Reader shielded = new NoCloseReader(rdr)) {
+ int index = 0;
+
+ int availLen = expected.length();
+ for (; index < (availLen / 2); index++) {
+ shielded.close();
+
+ int readValue = shielded.read();
+ if (readValue == -1) {
+ fail("Premature EOF after shield read of " + index + " bytes");
+ }
+
+ char expValue = expected.charAt(index);
+ char actValue = (char) (readValue & 0xFFFF);
+ if (expValue != actValue) {
+ fail("Mismatched shielded read value after " + index + " bytes");
+ }
+ }
+
+ for (; index < availLen; index++) {
+ int readValue = rdr.read();
+ if (readValue == -1) {
+ fail("Premature EOF after original read of " + index + " bytes");
+ }
+
+ char expValue = expected.charAt(index);
+ char actValue = (char) (readValue & 0xFFFF);
+ if (expValue != actValue) {
+ fail("Mismatched original read value after " + index + " bytes");
+ }
+ }
+
+ int readValue = shielded.read();
+ assertEquals("Shielded EOF not signalled", -1, readValue);
+
+ readValue = rdr.read();
+ assertEquals("Original EOF not signalled", -1, readValue);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java
new file mode 100644
index 0000000..b49678e
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Date;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class NoCloseWriterTest extends JUnitTestSupport {
+ public NoCloseWriterTest() {
+ super();
+ }
+
+ @Test
+ public void testCanKeepWritingAfterClose() throws IOException {
+ Path dir = createTempClassFolder();
+ Path file = dir.resolve(getCurrentTestName() + ".txt");
+ Files.deleteIfExists(file);
+
+ String expected = getClass().getName() + "#" + getCurrentTestName() + "@" + new Date();
+ try (OutputStream fileStream = Files.newOutputStream(file);
+ Writer w = new OutputStreamWriter(fileStream, StandardCharsets.UTF_8);
+ Writer shielded = new NoCloseWriter(w)) {
+ int index = 0;
+ int availLen = expected.length();
+ for (; index < (availLen / 2); index++) {
+ shielded.close();
+ shielded.write(expected.charAt(index));
+ }
+
+ w.write(expected, index, availLen - index);
+ }
+
+ byte[] actualBytes = Files.readAllBytes(file);
+ String actual = new String(actualBytes, StandardCharsets.UTF_8);
+ assertEquals(expected, actual);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java
new file mode 100644
index 0000000..31982da
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.EOFException;
+import java.io.IOException;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class NullInputStreamTest extends JUnitTestSupport {
+ private static final NullInputStream INSTANCE = new NullInputStream();
+
+ public NullInputStreamTest() {
+ super();
+ }
+
+ @Test
+ public void testReadOneChar() throws IOException {
+ assertEquals(-1, INSTANCE.read());
+ }
+
+ @Test
+ public void testReadFullBuffer() throws IOException {
+ assertEquals(-1, INSTANCE.read(new byte[Byte.SIZE]));
+ }
+
+ @Test
+ public void testReadPartialBuffer() throws IOException {
+ byte[] buf = new byte[Byte.SIZE];
+ assertEquals(-1, INSTANCE.read(buf, buf.length / 2, (buf.length / 2) - 1));
+ }
+
+ @Test
+ public void testSkip() throws IOException {
+ assertEquals(0L, INSTANCE.skip(Long.SIZE));
+ }
+
+ @Test
+ public void testAvailable() throws IOException {
+ assertEquals(0, INSTANCE.available());
+ }
+
+ @Test
+ public void testNotAllowedToAccessAfterClose() throws IOException {
+ NullInputStream stream = new NullInputStream();
+ stream.close();
+ assertFalse("Stream not marked as closed", stream.isOpen());
+
+ try {
+ int nRead = stream.read();
+ fail("Unexpected single byte read: " + nRead);
+ } catch (EOFException e) {
+ // expected
+ }
+
+ byte[] buf = new byte[Byte.SIZE];
+ try {
+ int nRead = stream.read(buf);
+ fail("Unexpected full buffer read: " + nRead);
+ } catch (EOFException e) {
+ // expected
+ }
+
+ try {
+ int nRead = stream.read(buf, buf.length / 2, (buf.length / 2) - 1);
+ fail("Unexpected partial buffer read: " + nRead);
+ } catch (EOFException e) {
+ // expected
+ }
+
+ try {
+ long skip = stream.skip(Long.SIZE);
+ fail("Unexpected skip result: " + skip);
+ } catch (EOFException e) {
+ // expected
+ }
+
+ try {
+ int nRead = stream.available();
+ fail("Unexpected available count: " + nRead);
+ } catch (IOException e) {
+ // expected
+ }
+ try {
+ stream.reset();
+ fail("Unexpected reset success");
+ } catch (EOFException e) {
+ // expected
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java
new file mode 100644
index 0000000..0fe845f
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class NullOutputStreamTest extends JUnitTestSupport {
+ public NullOutputStreamTest() {
+ super();
+ }
+
+ @Test
+ public void testNoAccessAllowedAfterClose() throws IOException {
+ NullOutputStream stream = new NullOutputStream();
+ stream.close();
+ assertFalse("Stream not marked as closed", stream.isOpen());
+
+ try {
+ stream.write('a');
+ fail("Unexpected single value write success");
+ } catch (EOFException e) {
+ // expected
+ }
+
+ byte[] buf = new byte[Byte.SIZE];
+ try {
+ Arrays.fill(buf, (byte) 0x41);
+ stream.write(buf);
+ fail("Unexpected full buffer write success");
+ } catch (EOFException e) {
+ // expected
+ }
+
+ try {
+ Arrays.fill(buf, (byte) 0x42);
+ stream.write(buf, buf.length / 2, (buf.length / 2) - 1);
+ fail("Unexpected partial buffer write success");
+ } catch (EOFException e) {
+ // expected
+ }
+
+ try {
+ stream.flush();
+ fail("Unexpected flush success");
+ } catch (EOFException e) {
+ // expected
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java
new file mode 100644
index 0000000..5f30529
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.io.der;
+
+import java.util.List;
+
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class ASN1ClassTest extends JUnitTestSupport {
+ private final ASN1Class expected;
+
+ public ASN1ClassTest(ASN1Class expected) {
+ this.expected = expected;
+ }
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> parameters() {
+ return parameterize(ASN1Class.VALUES);
+ }
+
+ @Test
+ public void testFromName() {
+ String name = expected.name();
+ for (int index = 1, count = name.length(); index <= count; index++) {
+ assertSame(name, expected, ASN1Class.fromName(name));
+ name = shuffleCase(name);
+ }
+ }
+
+ @Test // NOTE: this also tests "fromTypeValue" since "fromDERValue" invokes it
+ public void testFromDERValue() {
+ assertSame(expected, ASN1Class.fromDERValue((expected.getClassValue() << 6) & 0xFF));
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java
new file mode 100644
index 0000000..06034e2
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.io.der;
+
+import java.util.List;
+
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class ASN1TypeTest extends JUnitTestSupport {
+ private final ASN1Type expected;
+
+ public ASN1TypeTest(ASN1Type expected) {
+ this.expected = expected;
+ }
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> parameters() {
+ return parameterize(ASN1Type.VALUES);
+ }
+
+ @Test
+ public void testFromName() {
+ String name = expected.name();
+ for (int index = 1, count = name.length(); index <= count; index++) {
+ assertSame(name, expected, ASN1Type.fromName(name));
+ name = shuffleCase(name);
+ }
+ }
+
+ @Test
+ public void testFromTypeValue() {
+ assertSame(expected, ASN1Type.fromTypeValue(expected.getTypeValue()));
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java
new file mode 100644
index 0000000..ecc5b31
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.io.der;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class DERParserTest extends JUnitTestSupport {
+ public DERParserTest() {
+ super();
+ }
+
+ @Test
+ public void testReadLengthConstraint() throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ try (DERWriter w = new DERWriter(baos)) {
+ w.writeLength(DERParser.MAX_DER_VALUE_LENGTH + 1);
+ }
+ } finally {
+ baos.close();
+ }
+
+ try (DERParser parser = new DERParser(baos.toByteArray())) {
+ int len = parser.readLength();
+ fail("Unexpected success: len=" + len);
+ } catch (StreamCorruptedException e) {
+ // expected ignored
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/DERWriterTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/DERWriterTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/DERWriterTest.java
new file mode 100644
index 0000000..3064caa
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/der/DERWriterTest.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.io.der;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class DERWriterTest extends JUnitTestSupport {
+ public DERWriterTest() {
+ super();
+ }
+
+ @Test
+ public void testWriteStripLeadingZeroes() throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ try (DERWriter w = new DERWriter(baos)) {
+ w.writeBigInteger(BigInteger.valueOf(-1));
+ w.writeBigInteger(BigInteger.valueOf(129));
+ w.writeBigInteger(new byte[] {0, 0}, 0, 2);
+ w.writeBigInteger(new byte[] {0, 1}, 0, 2);
+ }
+ } finally {
+ baos.close();
+ }
+
+ try (DERParser parser = new DERParser(baos.toByteArray())) {
+ assertEquals(BigInteger.valueOf(255), parser.readBigInteger());
+ assertEquals(BigInteger.valueOf(129), parser.readBigInteger());
+ assertEquals(BigInteger.valueOf(0), parser.readBigInteger());
+ assertEquals(BigInteger.valueOf(1), parser.readBigInteger());
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/net/SshdSocketIpv6AddressTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/net/SshdSocketIpv6AddressTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/net/SshdSocketIpv6AddressTest.java
new file mode 100644
index 0000000..a38db34
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/net/SshdSocketIpv6AddressTest.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.net;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class SshdSocketIpv6AddressTest extends JUnitTestSupport {
+ public static final List<String> VALID_ADDRESSES =
+ Collections.unmodifiableList(
+ Arrays.asList(
+ "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:db8:85a3:0:0:8a2e:370:7334",
+ "2001:db8:85a3::8a2e:370:7334",
+ "2001:0db8::0001", "2001:db8::1",
+ "2001:db8:0:0:0:0:2:1", "2001:db8::2:1",
+ "2001:db8:0000:1:1:1:1:1", "2001:db8:0:1:1:1:1:1",
+ "2001:db8:85a3:8d3:1319:8a2e:370:7348",
+ "fe80::1ff:fe23:4567:890a", "fe80::1ff:fe23:4567:890a%eth2",
+ "fe80::1ff:fe23:4567:890a%3", "fe80:3::1ff:fe23:4567:890a",
+ "::ffff:c000:0280", "::ffff:192.0.2.128"));
+
+ private final String address;
+ private final boolean matches;
+
+ public SshdSocketIpv6AddressTest(String address, boolean matches) {
+ this.address = address;
+ this.matches = matches;
+ }
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> parameters() {
+ return Stream
+ .concat(SshdSocketAddress.WELL_KNOWN_IPV6_ADDRESSES.stream(), VALID_ADDRESSES.stream())
+ .map(address -> new Object[] {address, Boolean.TRUE})
+ .collect(Collectors.toList());
+ }
+
+ @Test
+ public void testIPv6AddressValidity() {
+ assertEquals(address, matches, SshdSocketAddress.isIPv6Address(address));
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName()
+ + "[address=" + address
+ + " , matches=" + matches
+ + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java
new file mode 100644
index 0000000..5d40c59
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.security;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.crypto.Cipher;
+
+import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.cipher.CipherInformation;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class SecurityProviderRegistrarCipherNameTest extends JUnitTestSupport {
+ private final CipherInformation cipherInfo;
+
+ public SecurityProviderRegistrarCipherNameTest(CipherInformation cipherInfo) {
+ this.cipherInfo = cipherInfo;
+ }
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> parameters() {
+ List<Object[]> params = new ArrayList<>();
+ for (CipherInformation cipherInfo : BuiltinCiphers.VALUES) {
+ String algorithm = cipherInfo.getAlgorithm();
+ String xform = cipherInfo.getTransformation();
+ if (!xform.startsWith(algorithm)) {
+ continue;
+ }
+
+ params.add(new Object[]{cipherInfo});
+ }
+ return params;
+ }
+
+ @Test
+ public void testGetEffectiveSecurityEntityName() {
+ String expected = cipherInfo.getAlgorithm();
+ String actual = SecurityProviderRegistrar.getEffectiveSecurityEntityName(Cipher.class, cipherInfo.getTransformation());
+ assertEquals("Mismatched pure cipher name", expected, actual);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java
new file mode 100644
index 0000000..9958916
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.security;
+
+import java.security.Provider;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.experimental.categories.Category;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@Category({ NoIoTestCase.class })
+public abstract class SecurityProviderRegistrarTestSupport extends JUnitTestSupport {
+ protected SecurityProviderRegistrarTestSupport() {
+ super();
+ }
+
+ public static Provider testGetSecurityProviderCaching(String prefix, SecurityProviderRegistrar registrar) {
+ return testGetSecurityProviderCaching(prefix, registrar, registrar.getSecurityProvider());
+ }
+
+ public static <P extends Provider> P testGetSecurityProviderCaching(String prefix, SecurityProviderRegistrar registrar, P expected) {
+ for (int index = 1; index <= Byte.SIZE; index++) {
+ Provider actual = registrar.getSecurityProvider();
+ assertSame(prefix + ": Mismatched provider instance at invocation #" + index, expected, actual);
+ }
+
+ return expected;
+ }
+
+ public static void assertSecurityEntitySupportState(
+ String prefix, SecurityProviderRegistrar registrar, boolean expected, String name, Class<?>... entities) {
+ assertSecurityEntitySupportState(prefix, registrar, expected, name, Arrays.asList(entities));
+ }
+
+ public static void assertSecurityEntitySupportState(
+ String prefix, SecurityProviderRegistrar registrar, boolean expected, String name, Collection<Class<?>> entities) {
+ for (Class<?> entity : entities) {
+ assertEquals(prefix + "[" + entity.getSimpleName() + "]", expected, registrar.isSecurityEntitySupported(entity, name));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java
new file mode 100644
index 0000000..46ca075
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/SecurityUtilsTest.java
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.security;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+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.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.sshd.common.cipher.BuiltinCiphers;
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceLoader;
+import org.apache.sshd.common.keyprovider.AbstractResourceKeyPairProvider;
+import org.apache.sshd.common.keyprovider.ClassLoadableResourceKeyPairProvider;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.AfterClass;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+@SuppressWarnings("checkstyle:MethodCount")
+public class SecurityUtilsTest extends JUnitTestSupport {
+ public static final String BC_NAMED_USAGE_PROP =
+ SecurityProviderRegistrar.CONFIG_PROP_BASE
+ + "." + SecurityUtils.BOUNCY_CASTLE
+ + "." + SecurityProviderRegistrar.NAMED_PROVIDER_PROPERTY;
+
+ private static final String DEFAULT_PASSWORD = "super secret passphrase";
+ private static final FilePasswordProvider TEST_PASSWORD_PROVIDER = file -> DEFAULT_PASSWORD;
+
+ public SecurityUtilsTest() {
+ super();
+ }
+
+ // NOTE: Using the BouncyCastle provider instead of the name does not work as expected so we take no chances
+ @BeforeClass
+ public static void useNamedBouncyCastleProvider() {
+ System.setProperty(BC_NAMED_USAGE_PROP, Boolean.TRUE.toString());
+ }
+
+ @AfterClass
+ public static void unsetBouncyCastleProviderUsagePreference() {
+ System.clearProperty(BC_NAMED_USAGE_PROP);
+ }
+
+ @Test
+ public void testLoadEncryptedDESPrivateKey() throws Exception {
+ testLoadEncryptedRSAPrivateKey("DES-EDE3");
+ }
+
+ @Test
+ public void testLoadEncryptedAESPrivateKey() {
+ for (BuiltinCiphers c : new BuiltinCiphers[]{
+ BuiltinCiphers.aes128cbc, BuiltinCiphers.aes192cbc, BuiltinCiphers.aes256cbc
+ }) {
+ if (!c.isSupported()) {
+ System.out.println("Skip unsupported encryption scheme: " + c.getName());
+ continue;
+ }
+
+ try {
+ testLoadEncryptedRSAPrivateKey("AES-" + c.getKeySize());
+ } catch (Exception e) {
+ fail("Failed (" + e.getClass().getSimpleName() + " to load key for " + c.getName() + ": " + e.getMessage());
+ }
+ }
+ }
+
+ private KeyPair testLoadEncryptedRSAPrivateKey(String algorithm) throws IOException, GeneralSecurityException {
+ return testLoadRSAPrivateKey(DEFAULT_PASSWORD.replace(' ', '-') + "-RSA-" + algorithm.toUpperCase() + "-key");
+ }
+
+ @Test
+ public void testLoadUnencryptedRSAPrivateKey() throws Exception {
+ testLoadRSAPrivateKey(getClass().getSimpleName() + "-RSA-KeyPair");
+ }
+
+ @Test
+ public void testLoadUnencryptedDSSPrivateKey() throws Exception {
+ testLoadDSSPrivateKey(getClass().getSimpleName() + "-DSA-KeyPair");
+ }
+
+ private KeyPair testLoadDSSPrivateKey(String name) throws Exception {
+ return testLoadPrivateKey(name, DSAPublicKey.class, DSAPrivateKey.class);
+ }
+
+ @Test
+ public void testLoadUnencryptedECPrivateKey() throws Exception {
+ Assume.assumeTrue("EC not supported", SecurityUtils.isECCSupported());
+ for (ECCurves c : ECCurves.VALUES) {
+ if (!c.isSupported()) {
+ System.out.println("Skip unsupported curve: " + c.getName());
+ continue;
+ }
+
+ testLoadECPrivateKey(getClass().getSimpleName() + "-EC-" + c.getKeySize() + "-KeyPair");
+ }
+ }
+
+ private KeyPair testLoadECPrivateKey(String name) throws IOException, GeneralSecurityException {
+ return testLoadPrivateKey(name, ECPublicKey.class, ECPrivateKey.class);
+ }
+
+ private KeyPair testLoadRSAPrivateKey(String name) throws IOException, GeneralSecurityException {
+ return testLoadPrivateKey(name, RSAPublicKey.class, RSAPrivateKey.class);
+ }
+
+ private KeyPair testLoadPrivateKey(String name, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType)
+ throws IOException, GeneralSecurityException {
+ Path folder = getTestResourcesFolder();
+ Path file = folder.resolve(name);
+ KeyPair kpFile = testLoadPrivateKeyFile(file, pubType, prvType);
+ if (SecurityUtils.isBouncyCastleRegistered()) {
+ KeyPairResourceLoader bcLoader = SecurityUtils.getBouncycastleKeyPairResourceParser();
+ Collection<KeyPair> kpList = bcLoader.loadKeyPairs(file, TEST_PASSWORD_PROVIDER);
+ assertEquals(name + ": Mismatched loaded BouncyCastle keys count", 1, GenericUtils.size(kpList));
+
+ KeyPair kpBC = kpList.iterator().next();
+ assertTrue(name + ": Mismatched BouncyCastle vs. file values", KeyUtils.compareKeyPairs(kpFile, kpBC));
+ }
+
+ Class<?> clazz = getClass();
+ Package pkg = clazz.getPackage();
+ KeyPair kpResource = testLoadPrivateKeyResource(pkg.getName().replace('.', '/') + "/" + name, pubType, prvType);
+ assertTrue(name + ": Mismatched key file vs. resource values", KeyUtils.compareKeyPairs(kpFile, kpResource));
+ return kpResource;
+ }
+
+ private static KeyPair testLoadPrivateKeyResource(String name, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
+ return testLoadPrivateKey(name, new ClassLoadableResourceKeyPairProvider(name), pubType, prvType);
+ }
+
+ private static KeyPair testLoadPrivateKeyFile(Path file, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
+ return testLoadPrivateKey(file.toString(), new FileKeyPairProvider(file), pubType, prvType);
+ }
+
+ private static KeyPair testLoadPrivateKey(String resourceKey, AbstractResourceKeyPairProvider<?> provider,
+ Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
+ provider.setPasswordFinder(TEST_PASSWORD_PROVIDER);
+ Iterable<KeyPair> iterator = provider.loadKeys();
+ List<KeyPair> pairs = new ArrayList<>();
+ for (KeyPair kp : iterator) {
+ pairs.add(kp);
+ }
+
+ assertEquals("Mismatched loaded pairs count for " + resourceKey, 1, pairs.size());
+
+ KeyPair kp = pairs.get(0);
+ PublicKey pub = kp.getPublic();
+ assertNotNull("No public key extracted", pub);
+ assertTrue("Not an " + pubType.getSimpleName() + " public key for " + resourceKey, pubType.isAssignableFrom(pub.getClass()));
+
+ PrivateKey prv = kp.getPrivate();
+ assertNotNull("No private key extracted", prv);
+ assertTrue("Not an " + prvType.getSimpleName() + " private key for " + resourceKey, prvType.isAssignableFrom(prv.getClass()));
+
+ return kp;
+ }
+
+ @Test
+ public void testSetMaxDHGroupExchangeKeySizeByProperty() {
+ try {
+ for (int expected = SecurityUtils.MIN_DHGEX_KEY_SIZE; expected <= SecurityUtils.MAX_DHGEX_KEY_SIZE; expected += 1024) {
+ SecurityUtils.setMaxDHGroupExchangeKeySize(0); // force detection
+ try {
+ System.setProperty(SecurityUtils.MAX_DHGEX_KEY_SIZE_PROP, Integer.toString(expected));
+ assertTrue("DH group not supported for key size=" + expected, SecurityUtils.isDHGroupExchangeSupported());
+ assertEquals("Mismatched values", expected, SecurityUtils.getMaxDHGroupExchangeKeySize());
+ } finally {
+ System.clearProperty(SecurityUtils.MAX_DHGEX_KEY_SIZE_PROP);
+ }
+ }
+ } finally {
+ SecurityUtils.setMaxDHGroupExchangeKeySize(0); // force detection
+ }
+ }
+
+ @Test
+ public void testSetMaxDHGroupExchangeKeySizeProgrammatically() {
+ try {
+ for (int expected = SecurityUtils.MIN_DHGEX_KEY_SIZE; expected <= SecurityUtils.MAX_DHGEX_KEY_SIZE; expected += 1024) {
+ SecurityUtils.setMaxDHGroupExchangeKeySize(expected);
+ assertTrue("DH group not supported for key size=" + expected, SecurityUtils.isDHGroupExchangeSupported());
+ assertEquals("Mismatched values", expected, SecurityUtils.getMaxDHGroupExchangeKeySize());
+ }
+ } finally {
+ SecurityUtils.setMaxDHGroupExchangeKeySize(0); // force detection
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java
new file mode 100644
index 0000000..7da51c5
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.security.eddsa;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+import net.i2p.crypto.eddsa.EdDSAEngine;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class EDDSAProviderTest extends JUnitTestSupport {
+ private static KeyPair keyPair;
+
+ public EDDSAProviderTest() {
+ super();
+ }
+
+ @BeforeClass
+ public static void checkProviderSupported() throws GeneralSecurityException {
+ Assume.assumeTrue(SecurityUtils.EDDSA + " not supported", SecurityUtils.isEDDSACurveSupported());
+ KeyPairGenerator g = SecurityUtils.getKeyPairGenerator(SecurityUtils.EDDSA);
+ assertNotNull("No generator instance", g);
+
+ keyPair = g.generateKeyPair();
+ assertNotNull("No key pair generated", keyPair);
+
+ PublicKey pubKey = keyPair.getPublic();
+ assertNotNull("No public key", pubKey);
+ assertEquals("Mismatched public key algorithm", SecurityUtils.EDDSA, pubKey.getAlgorithm());
+ assertEquals("Mismatched public key type", KeyPairProvider.SSH_ED25519, KeyUtils.getKeyType(pubKey));
+
+ PrivateKey prvKey = keyPair.getPrivate();
+ assertNotNull("No private key", prvKey);
+ assertEquals("Mismatched key-pair algorithm", pubKey.getAlgorithm(), prvKey.getAlgorithm());
+ assertEquals("Mismatched private key type", KeyPairProvider.SSH_ED25519, KeyUtils.getKeyType(prvKey));
+ }
+
+ @Test
+ public void testSignature() throws GeneralSecurityException {
+ Signature s = SecurityUtils.getSignature(EdDSAEngine.SIGNATURE_ALGORITHM);
+ assertNotNull("No signature instance", s);
+ s.initSign(keyPair.getPrivate());
+
+ byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
+ s.update(data);
+ byte[] signed = s.sign();
+
+ s = SecurityUtils.getSignature(EdDSAEngine.SIGNATURE_ALGORITHM);
+ s.initVerify(keyPair.getPublic());
+ s.update(data);
+ assertTrue("Failed to verify", s.verify(signed));
+ }
+
+ @Test
+ public void testPublicKeyEntryDecoder() throws IOException, GeneralSecurityException {
+ String comment = getCurrentTestName() + "@" + getClass().getSimpleName();
+ String expected = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGPKSUTyz1HwHReFVvD5obVsALAgJRNarH4TRpNePnAS " + comment;
+ AuthorizedKeyEntry keyEntry = AuthorizedKeyEntry.parseAuthorizedKeyEntry(expected);
+ assertNotNull("No extracted key entry", keyEntry);
+
+ assertEquals("Mismatched key type", KeyPairProvider.SSH_ED25519, keyEntry.getKeyType());
+ assertEquals("Mismatched comment", comment, keyEntry.getComment());
+
+ StringBuilder sb = new StringBuilder(expected.length());
+ PublicKey pubKey = keyEntry.appendPublicKey(sb, null);
+ assertEquals("Mismatched encoded result", expected, sb.toString());
+
+ testPublicKeyRecovery(pubKey);
+ }
+
+ @Test
+ public void testGeneratedPublicKeyRecovery() throws IOException, GeneralSecurityException {
+ testPublicKeyRecovery(keyPair.getPublic());
+ }
+
+ private void testPublicKeyRecovery(PublicKey pubKey) throws IOException, GeneralSecurityException {
+ assertNotNull("No public key generated", pubKey);
+ assertEquals("Mismatched public key algorithm", SecurityUtils.EDDSA, pubKey.getAlgorithm());
+
+ Buffer buf = SecurityUtils.putRawEDDSAPublicKey(new ByteArrayBuffer(), pubKey);
+ PublicKey actual = buf.getRawPublicKey();
+ assertEquals("Mismatched key algorithm", pubKey.getAlgorithm(), actual.getAlgorithm());
+ assertEquals("Mismatched recovered key", pubKey, actual);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/Ed25519VectorsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/Ed25519VectorsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/Ed25519VectorsTest.java
new file mode 100644
index 0000000..cfebb85
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/Ed25519VectorsTest.java
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.security.eddsa;
+
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.sshd.common.signature.Signature;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+import net.i2p.crypto.eddsa.EdDSAPrivateKey;
+import net.i2p.crypto.eddsa.EdDSAPublicKey;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-02#section-6">
+ * EdDSA and Ed25519 draft-josefsson-eddsa-ed25519-02 - section 6 - Test Vectors for Ed25519</A>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class Ed25519VectorsTest extends JUnitTestSupport {
+ private final byte[] prvBytes;
+ private final PrivateKey privateKey;
+ private final byte[] pubBytes;
+ private final PublicKey publicKey;
+ private final byte[] msgBytes;
+ private final byte[] expSignature;
+
+ public Ed25519VectorsTest(String name, String prvKey, String pubKey, String msg, String signature)
+ throws GeneralSecurityException {
+ prvBytes = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, prvKey);
+ privateKey = EdDSASecurityProviderUtils.generateEDDSAPrivateKey(prvBytes.clone());
+ pubBytes = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, pubKey);
+ publicKey = EdDSASecurityProviderUtils.generateEDDSAPublicKey(pubBytes.clone());
+ msgBytes = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, msg);
+ expSignature = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, signature);
+ }
+
+ @Parameters(name = "{0}")
+ @SuppressWarnings("checkstyle:anoninnerlength")
+ public static List<Object[]> parameters() {
+ return new ArrayList<>(Arrays.asList(
+ new Object[]{
+ "TEST1 - empty message",
+ "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
+ "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
+ "",
+ "e5564300c360ac729086e2cc806e828a"
+ + "84877f1eb8e5d974d873e06522490155"
+ + "5fb8821590a33bacc61e39701cf9b46b"
+ + "d25bf5f0595bbe24655141438e7a100b"
+ },
+ new Object[]{
+ "TEST2 - one byte",
+ "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
+ "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
+ "72",
+ "92a009a9f0d4cab8720e820b5f642540"
+ + "a2b27b5416503f8fb3762223ebdb69da"
+ + "085ac1e43e15996e458f3613d0f11d8c"
+ + "387b2eaeb4302aeeb00d291612bb0c00"
+ },
+ new Object[]{
+ "TEST3 - 2 bytes",
+ "c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7",
+ "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025",
+ "af82",
+ "6291d657deec24024827e69c3abe01a3"
+ + "0ce548a284743a445e3680d7db5ac3ac"
+ + "18ff9b538d16f290ae67f760984dc659"
+ + "4a7c15e9716ed28dc027beceea1ec40a"
+ },
+ new Object[]{
+ "TEST1024 - large message",
+ "f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5",
+ "278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e",
+ "08b8b2b733424243760fe426a4b54908"
+ + "632110a66c2f6591eabd3345e3e4eb98"
+ + "fa6e264bf09efe12ee50f8f54e9f77b1"
+ + "e355f6c50544e23fb1433ddf73be84d8"
+ + "79de7c0046dc4996d9e773f4bc9efe57"
+ + "38829adb26c81b37c93a1b270b20329d"
+ + "658675fc6ea534e0810a4432826bf58c"
+ + "941efb65d57a338bbd2e26640f89ffbc"
+ + "1a858efcb8550ee3a5e1998bd177e93a"
+ + "7363c344fe6b199ee5d02e82d522c4fe"
+ + "ba15452f80288a821a579116ec6dad2b"
+ + "3b310da903401aa62100ab5d1a36553e"
+ + "06203b33890cc9b832f79ef80560ccb9"
+ + "a39ce767967ed628c6ad573cb116dbef"
+ + "efd75499da96bd68a8a97b928a8bbc10"
+ + "3b6621fcde2beca1231d206be6cd9ec7"
+ + "aff6f6c94fcd7204ed3455c68c83f4a4"
+ + "1da4af2b74ef5c53f1d8ac70bdcb7ed1"
+ + "85ce81bd84359d44254d95629e9855a9"
+ + "4a7c1958d1f8ada5d0532ed8a5aa3fb2"
+ + "d17ba70eb6248e594e1a2297acbbb39d"
+ + "502f1a8c6eb6f1ce22b3de1a1f40cc24"
+ + "554119a831a9aad6079cad88425de6bd"
+ + "e1a9187ebb6092cf67bf2b13fd65f270"
+ + "88d78b7e883c8759d2c4f5c65adb7553"
+ + "878ad575f9fad878e80a0c9ba63bcbcc"
+ + "2732e69485bbc9c90bfbd62481d9089b"
+ + "eccf80cfe2df16a2cf65bd92dd597b07"
+ + "07e0917af48bbb75fed413d238f5555a"
+ + "7a569d80c3414a8d0859dc65a46128ba"
+ + "b27af87a71314f318c782b23ebfe808b"
+ + "82b0ce26401d2e22f04d83d1255dc51a"
+ + "ddd3b75a2b1ae0784504df543af8969b"
+ + "e3ea7082ff7fc9888c144da2af58429e"
+ + "c96031dbcad3dad9af0dcbaaaf268cb8"
+ + "fcffead94f3c7ca495e056a9b47acdb7"
+ + "51fb73e666c6c655ade8297297d07ad1"
+ + "ba5e43f1bca32301651339e22904cc8c"
+ + "42f58c30c04aafdb038dda0847dd988d"
+ + "cda6f3bfd15c4b4c4525004aa06eeff8"
+ + "ca61783aacec57fb3d1f92b0fe2fd1a8"
+ + "5f6724517b65e614ad6808d6f6ee34df"
+ + "f7310fdc82aebfd904b01e1dc54b2927"
+ + "094b2db68d6f903b68401adebf5a7e08"
+ + "d78ff4ef5d63653a65040cf9bfd4aca7"
+ + "984a74d37145986780fc0b16ac451649"
+ + "de6188a7dbdf191f64b5fc5e2ab47b57"
+ + "f7f7276cd419c17a3ca8e1b939ae49e4"
+ + "88acba6b965610b5480109c8b17b80e1"
+ + "b7b750dfc7598d5d5011fd2dcc5600a3"
+ + "2ef5b52a1ecc820e308aa342721aac09"
+ + "43bf6686b64b2579376504ccc493d97e"
+ + "6aed3fb0f9cd71a43dd497f01f17c0e2"
+ + "cb3797aa2a2f256656168e6c496afc5f"
+ + "b93246f6b1116398a346f1a641f3b041"
+ + "e989f7914f90cc2c7fff357876e506b5"
+ + "0d334ba77c225bc307ba537152f3f161"
+ + "0e4eafe595f6d9d90d11faa933a15ef1"
+ + "369546868a7f3a45a96768d40fd9d034"
+ + "12c091c6315cf4fde7cb68606937380d"
+ + "b2eaaa707b4c4185c32eddcdd306705e"
+ + "4dc1ffc872eeee475a64dfac86aba41c"
+ + "0618983f8741c5ef68d3a101e8a3b8ca"
+ + "c60c905c15fc910840b94c00a0b9d0",
+ "0aab4c900501b3e24d7cdf4663326a3a"
+ + "87df5e4843b2cbdb67cbf6e460fec350"
+ + "aa5371b1508f9f4528ecea23c436d94b"
+ + "5e8fcd4f681e30a6ac00a9704a188a03"
+ }));
+ }
+
+ @BeforeClass
+ public static void checkEDDSASupported() {
+ Assume.assumeTrue("EDDSA N/A", SecurityUtils.isEDDSACurveSupported());
+ }
+
+ @Test
+ public void testPublicKeyBytes() {
+ byte[] publicSeed = Ed25519PublicKeyDecoder.getSeedValue((EdDSAPublicKey) publicKey);
+ assertArrayEquals("Mismatched public seed value", pubBytes, publicSeed);
+ }
+
+ @Test
+ public void testPrivateKeyBytes() {
+ assertArrayEquals("Mismatched private seed value", prvBytes, ((EdDSAPrivateKey) privateKey).getSeed());
+ }
+
+ @Test
+ public void testSignature() throws Exception {
+ Signature signer = EdDSASecurityProviderUtils.getEDDSASignature();
+ signer.initSigner(privateKey);
+ signer.update(msgBytes.clone());
+
+ byte[] actSignature = signer.sign();
+ assertArrayEquals("Mismatched signature", expSignature, actSignature);
+
+ Signature verifier = EdDSASecurityProviderUtils.getEDDSASignature();
+ verifier.initVerifier(publicKey);
+ verifier.update(msgBytes.clone());
+ assertTrue("Verification failed", verifier.verify(expSignature));
+ }
+
+ @Test
+ public void testPartialBufferSignature() throws Exception {
+ byte[] extraData = getCurrentTestName().getBytes(StandardCharsets.UTF_8);
+ byte[] dataBuf = new byte[msgBytes.length + extraData.length];
+ int offset = extraData.length / 2;
+ System.arraycopy(extraData, 0, dataBuf, 0, offset);
+ System.arraycopy(msgBytes, 0, dataBuf, offset, msgBytes.length);
+ System.arraycopy(extraData, offset, dataBuf, offset + msgBytes.length, extraData.length - offset);
+
+ Signature signer = EdDSASecurityProviderUtils.getEDDSASignature();
+ signer.initSigner(privateKey);
+ signer.update(dataBuf.clone(), offset, msgBytes.length);
+
+ byte[] actSignature = signer.sign();
+ assertArrayEquals("Mismatched signature", expSignature, actSignature);
+
+ Signature verifier = EdDSASecurityProviderUtils.getEDDSASignature();
+ verifier.initVerifier(publicKey);
+ verifier.update(dataBuf.clone(), offset, msgBytes.length);
+ assertTrue("Verification failed", verifier.verify(expSignature));
+ }
+}
[03/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
index 5e7b360..b58cfb2 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpTest.java
@@ -99,8 +99,8 @@ import org.apache.sshd.server.subsystem.sftp.SftpEventListenerManager;
import org.apache.sshd.server.subsystem.sftp.SftpFileSystemAccessor;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemEnvironment;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.apache.sshd.util.test.SimpleUserInfo;
-import org.apache.sshd.util.test.Utils;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
@@ -145,7 +145,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
public void testWriteOffsetIgnoredForAppendMode() throws IOException {
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
Path testFile = assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
Files.deleteIfExists(testFile);
@@ -160,7 +160,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
session.auth().verify(5L, TimeUnit.SECONDS);
try (SftpClient sftp = createSftpClient(session)) {
- String file = Utils.resolveRelativeRemotePath(parentPath, testFile);
+ String file = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, testFile);
try (CloseableHandle handle = sftp.open(file, OpenMode.Create, OpenMode.Write, OpenMode.Read, OpenMode.Append)) {
sftp.write(handle, 7365L, expectedRandom);
@@ -192,7 +192,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
public void testReadBufferLimit() throws Exception {
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
Path testFile = assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
byte[] expected = new byte[1024];
@@ -206,7 +206,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
session.auth().verify(5L, TimeUnit.SECONDS);
try (SftpClient sftp = createSftpClient(session)) {
- String file = Utils.resolveRelativeRemotePath(parentPath, testFile);
+ String file = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, testFile);
byte[] actual = new byte[expected.length];
int maxAllowed = actual.length / 4;
// allow less than actual
@@ -263,7 +263,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
private void testCannotEscapeRoot(boolean useAbsolutePath) throws Exception {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
assertHierarchyTargetFolderExists(lclSftp);
sshd.setFileSystemFactory(new VirtualFileSystemFactory(lclSftp));
@@ -327,9 +327,9 @@ public class SftpTest extends AbstractSftpClientTestSupport {
public void testNormalizeRemotePathsValues() throws Exception {
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
Path testFile = assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
- String file = Utils.resolveRelativeRemotePath(parentPath, testFile);
+ String file = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, testFile);
String[] comps = GenericUtils.split(file, '/');
Factory<? extends Random> factory = client.getRandomFactory();
@@ -379,10 +379,10 @@ public class SftpTest extends AbstractSftpClientTestSupport {
public void testOpen() throws Exception {
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
Path clientFolder = lclSftp.resolve("client");
Path testFile = clientFolder.resolve("file.txt");
- String file = Utils.resolveRelativeRemotePath(parentPath, testFile);
+ String file = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, testFile);
File javaFile = testFile.toFile();
assertHierarchyTargetFolderExists(javaFile.getParentFile());
@@ -477,7 +477,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
public void testInputStreamSkipAndReset() throws Exception {
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path localFile = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ Path localFile = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
Files.createDirectories(localFile.getParent());
byte[] data = (getClass().getName() + "#" + getCurrentTestName() + "[" + localFile + "]").getBytes(StandardCharsets.UTF_8);
Files.write(localFile, data, StandardOpenOption.CREATE);
@@ -486,7 +486,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
session.auth().verify(5L, TimeUnit.SECONDS);
try (SftpClient sftp = createSftpClient(session);
- InputStream stream = sftp.read(Utils.resolveRelativeRemotePath(parentPath, localFile), OpenMode.Read)) {
+ InputStream stream = sftp.read(CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, localFile), OpenMode.Read)) {
assertFalse("Stream reported mark supported", stream.markSupported());
try {
stream.mark(data.length);
@@ -563,7 +563,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path localFile = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ Path localFile = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
Files.createDirectories(localFile.getParent());
byte[] expected = (getClass().getName() + "#" + getCurrentTestName() + "[" + localFile + "]").getBytes(StandardCharsets.UTF_8);
Files.write(localFile, expected, StandardOpenOption.CREATE);
@@ -573,7 +573,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
try (SftpClient sftp = createSftpClient(session)) {
byte[] actual = new byte[expected.length];
- try (InputStream stream = sftp.read(Utils.resolveRelativeRemotePath(parentPath, localFile), OpenMode.Read)) {
+ try (InputStream stream = sftp.read(CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, localFile), OpenMode.Read)) {
IoUtils.readFully(stream, actual);
}
@@ -584,7 +584,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
Path localParent = localFile.getParent();
String localName = Objects.toString(localFile.getFileName(), null);
- try (CloseableHandle handle = sftp.openDir(Utils.resolveRelativeRemotePath(parentPath, localParent))) {
+ try (CloseableHandle handle = sftp.openDir(CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, localParent))) {
List<DirEntry> entries = sftp.readDir(handle);
Path remoteParent = dirHolder.getAndSet(null);
assertNotNull("No remote folder holder value", remoteParent);
@@ -824,12 +824,12 @@ public class SftpTest extends AbstractSftpClientTestSupport {
session.auth().verify(5L, TimeUnit.SECONDS);
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(lclSftp);
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(lclSftp);
Path parentPath = targetPath.getParent();
Path clientFolder = assertHierarchyTargetFolderExists(lclSftp).resolve("client");
- String dir = Utils.resolveRelativeRemotePath(parentPath, clientFolder);
+ String dir = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder);
try (SftpClient sftp = createSftpClient(session)) {
sftp.mkdir(dir);
@@ -848,7 +848,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
// test erroneous calls that check for negative values
Path invalidPath = clientFolder.resolve(getCurrentTestName() + "-invalid");
- testInvalidParams(sftp, invalidPath, Utils.resolveRelativeRemotePath(parentPath, invalidPath));
+ testInvalidParams(sftp, invalidPath, CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, invalidPath));
// cleanup
sftp.rmdir(dir);
@@ -920,11 +920,11 @@ public class SftpTest extends AbstractSftpClientTestSupport {
String d = getCurrentTestName() + "\n";
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(lclSftp);
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(lclSftp);
Path target = assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
- String remotePath = Utils.resolveRelativeRemotePath(targetPath.getParent(), target);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(targetPath.getParent(), target);
final int numIterations = 10;
StringBuilder sb = new StringBuilder(d.length() * numIterations * numIterations);
@@ -946,11 +946,11 @@ public class SftpTest extends AbstractSftpClientTestSupport {
@Test
public void testReadWriteWithOffset() throws Exception {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(lclSftp);
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(lclSftp);
Path localPath = assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
- String remotePath = Utils.resolveRelativeRemotePath(targetPath.getParent(), localPath);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(targetPath.getParent(), localPath);
String data = getCurrentTestName();
String extraData = "@" + getClass().getSimpleName();
int appendOffset = -5;
@@ -1024,8 +1024,8 @@ public class SftpTest extends AbstractSftpClientTestSupport {
@Test
public void testRename() throws Exception {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(lclSftp);
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(lclSftp);
Path parentPath = targetPath.getParent();
Path clientFolder = assertHierarchyTargetFolderExists(lclSftp.resolve("client"));
@@ -1035,15 +1035,15 @@ public class SftpTest extends AbstractSftpClientTestSupport {
try (SftpClient sftp = createSftpClient(session)) {
Path file1 = clientFolder.resolve("file-1.txt");
- String file1Path = Utils.resolveRelativeRemotePath(parentPath, file1);
+ String file1Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, file1);
try (OutputStream os = sftp.write(file1Path, SftpClient.MIN_WRITE_BUFFER_SIZE)) {
os.write((getCurrentTestName() + "\n").getBytes(StandardCharsets.UTF_8));
}
Path file2 = clientFolder.resolve("file-2.txt");
- String file2Path = Utils.resolveRelativeRemotePath(parentPath, file2);
+ String file2Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, file2);
Path file3 = clientFolder.resolve("file-3.txt");
- String file3Path = Utils.resolveRelativeRemotePath(parentPath, file3);
+ String file3Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, file3);
try {
sftp.rename(file2Path, file3Path);
fail("Unxpected rename success of " + file2Path + " => " + file3Path);
@@ -1223,12 +1223,12 @@ public class SftpTest extends AbstractSftpClientTestSupport {
private void testClient(FactoryManager manager, SftpClient sftp) throws Exception {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(lclSftp);
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(lclSftp);
Path parentPath = targetPath.getParent();
Path clientFolder = assertHierarchyTargetFolderExists(lclSftp).resolve("client");
- String dir = Utils.resolveRelativeRemotePath(parentPath, clientFolder);
+ String dir = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder);
sftp.mkdir(dir);
String file = dir + "/" + getCurrentTestName() + "-file.txt";
@@ -1343,8 +1343,8 @@ public class SftpTest extends AbstractSftpClientTestSupport {
};
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(lclSftp);
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(lclSftp);
/*
* NOTE !!! according to Jsch documentation
@@ -1360,7 +1360,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
*/
Path parentPath = targetPath.getParent();
Path sourcePath = assertHierarchyTargetFolderExists(lclSftp).resolve("src.txt");
- String remSrcPath = "/" + Utils.resolveRelativeRemotePath(parentPath, sourcePath);
+ String remSrcPath = "/" + CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, sourcePath);
factory.addSftpEventListener(listener);
try {
@@ -1376,7 +1376,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
assertEquals("Mismatched stored data in " + remSrcPath, data, readFile(remSrcPath));
Path linkPath = lclSftp.resolve("link-" + sourcePath.getFileName());
- String remLinkPath = "/" + Utils.resolveRelativeRemotePath(parentPath, linkPath);
+ String remLinkPath = "/" + CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, linkPath);
LinkOption[] options = IoUtils.getLinkOptions(false);
if (Files.exists(linkPath, options)) {
Files.delete(linkPath);
@@ -1405,7 +1405,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
@Test // see SSHD-697
public void testFileChannel() throws IOException {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
Path lclFile = lclSftp.resolve(getCurrentTestName() + ".txt");
Files.deleteIfExists(lclFile);
byte[] expected = (getClass().getName() + "#" + getCurrentTestName() + "(" + new Date() + ")").getBytes(StandardCharsets.UTF_8);
@@ -1416,7 +1416,7 @@ public class SftpTest extends AbstractSftpClientTestSupport {
try (SftpClient sftp = createSftpClient(session)) {
Path parentPath = targetPath.getParent();
- String remFilePath = Utils.resolveRelativeRemotePath(parentPath, lclFile);
+ String remFilePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclFile);
try (FileChannel fc = sftp.openRemotePathChannel(remFilePath, EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE))) {
int writeLen = fc.write(ByteBuffer.wrap(expected));
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelectorTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelectorTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelectorTest.java
index afc1944..e02aa30 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelectorTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionSelectorTest.java
@@ -27,7 +27,7 @@ import java.util.Random;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemEnvironment;
-import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.JUnitTestSupport;
import org.apache.sshd.util.test.NoIoTestCase;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -40,7 +40,7 @@ import org.mockito.Mockito;
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Category({ NoIoTestCase.class })
-public class SftpVersionSelectorTest extends BaseTestSupport {
+public class SftpVersionSelectorTest extends JUnitTestSupport {
public SftpVersionSelectorTest() {
super();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java
index 0e82f6e..1051c50 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java
@@ -61,8 +61,8 @@ import org.apache.sshd.server.subsystem.sftp.SftpEventListener;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystem;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemEnvironment;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.Utils;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -111,13 +111,13 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
@Test // See SSHD-749
public void testSftpOpenFlags() throws Exception {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
Path lclParent = assertHierarchyTargetFolderExists(lclSftp);
Path lclFile = lclParent.resolve(getCurrentTestName() + "-" + getTestedVersion() + ".txt");
Files.deleteIfExists(lclFile);
Path parentPath = targetPath.getParent();
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclFile);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclFile);
try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
.verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.getSession()) {
@@ -150,11 +150,11 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
@Test // see SSHD-572
public void testSftpFileTimesUpdate() throws Exception {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
Path lclFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + "-" + getTestedVersion() + ".txt");
Files.write(lclFile, getClass().getName().getBytes(StandardCharsets.UTF_8));
Path parentPath = targetPath.getParent();
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclFile);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclFile);
try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
.verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.getSession()) {
@@ -186,7 +186,7 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
@Test // see SSHD-573
public void testSftpFileTypeAndPermissionsUpdate() throws Exception {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
Path subFolder = Files.createDirectories(lclSftp.resolve("sub-folder"));
String subFolderName = subFolder.getFileName().toString();
Path lclFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + "-" + getTestedVersion() + ".txt");
@@ -194,7 +194,7 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
Files.write(lclFile, getClass().getName().getBytes(StandardCharsets.UTF_8));
Path parentPath = targetPath.getParent();
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclSftp);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclSftp);
try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
.verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.getSession()) {
@@ -299,13 +299,13 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
});
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
Files.createDirectories(lclSftp.resolve("sub-folder"));
Path lclFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + "-" + getTestedVersion() + ".txt");
Files.write(lclFile, getClass().getName().getBytes(StandardCharsets.UTF_8));
Path parentPath = targetPath.getParent();
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclSftp);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclSftp);
int numInvoked = 0;
List<NamedFactory<Command>> factories = sshd.getSubsystemFactories();
@@ -418,13 +418,13 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
});
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
Files.createDirectories(lclSftp.resolve("sub-folder"));
Path lclFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + "-" + getTestedVersion() + ".txt");
Files.write(lclFile, getClass().getName().getBytes(StandardCharsets.UTF_8));
Path parentPath = targetPath.getParent();
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclSftp);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclSftp);
int numInvoked = 0;
List<NamedFactory<Command>> factories = sshd.getSubsystemFactories();
@@ -471,7 +471,7 @@ public class SftpVersionsTest extends AbstractSftpClientTestSupport {
int version = sftp.getVersion();
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, targetPath);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, targetPath);
try (CloseableHandle handle = sftp.openDir(remotePath)) {
List<DirEntry> entries = sftp.readDir(handle, eolIndicator);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SimpleSftpClientTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SimpleSftpClientTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SimpleSftpClientTest.java
index 2f41c0f..7663485 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SimpleSftpClientTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SimpleSftpClientTest.java
@@ -33,7 +33,7 @@ import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.subsystem.sftp.SftpConstants;
import org.apache.sshd.common.util.io.IoUtils;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.apache.sshd.util.test.client.simple.BaseSimpleClientTestSupport;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -80,11 +80,11 @@ public class SimpleSftpClientTest extends BaseSimpleClientTestSupport {
@Test
public void testSftpProxyCalls() throws Exception {
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(lclSftp);
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(lclSftp);
Path clientFolder = assertHierarchyTargetFolderExists(lclSftp).resolve("client");
Path clientFile = clientFolder.resolve("file.txt");
- String remoteFileDir = Utils.resolveRelativeRemotePath(parentPath, clientFolder);
+ String remoteFileDir = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder);
String clientFileName = clientFile.getFileName().toString();
String remoteFilePath = remoteFileDir + "/" + clientFileName;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java
index e3537ea..1ddd789 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java
@@ -49,8 +49,8 @@ import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.NumberUtils;
import org.apache.sshd.common.util.buffer.BufferUtils;
import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.Utils;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -159,7 +159,7 @@ public class AbstractCheckFileExtensionTest extends AbstractSftpClientTestSuppor
@SuppressWarnings("checkstyle:nestedtrydepth")
private void testCheckFileExtension(NamedFactory<? extends Digest> factory, byte[] data, int hashBlockSize, byte[] expectedHash) throws Exception {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
Path srcFile = assertHierarchyTargetFolderExists(lclSftp).resolve(factory.getName() + "-data-" + data.length + "-" + hashBlockSize + ".txt");
Files.write(srcFile, data, IoUtils.EMPTY_OPEN_OPTIONS);
@@ -175,8 +175,8 @@ public class AbstractCheckFileExtensionTest extends AbstractSftpClientTestSuppor
}
Path parentPath = targetPath.getParent();
- String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile);
- String srcFolder = Utils.resolveRelativeRemotePath(parentPath, srcFile.getParent());
+ String srcPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile);
+ String srcFolder = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile.getParent());
try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
session.addPasswordIdentity(getCurrentTestName());
session.auth().verify(5L, TimeUnit.SECONDS);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtensionTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtensionTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtensionTest.java
index ea2783a..c195f15 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtensionTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtensionTest.java
@@ -43,8 +43,8 @@ import org.apache.sshd.common.subsystem.sftp.SftpException;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.buffer.BufferUtils;
import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.Utils;
import org.junit.Assume;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -127,13 +127,13 @@ public class AbstractMD5HashExtensionTest extends AbstractSftpClientTestSupport
}
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
Path srcFile = assertHierarchyTargetFolderExists(lclSftp).resolve("data-" + data.length + ".txt");
Files.write(srcFile, data, IoUtils.EMPTY_OPEN_OPTIONS);
Path parentPath = targetPath.getParent();
- String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile);
- String srcFolder = Utils.resolveRelativeRemotePath(parentPath, srcFile.getParent());
+ String srcPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile);
+ String srcFolder = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile.getParent());
try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
session.addPasswordIdentity(getCurrentTestName());
session.auth().verify(5L, TimeUnit.SECONDS);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyDataExtensionImplTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyDataExtensionImplTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyDataExtensionImplTest.java
index 01d3334..6b78607 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyDataExtensionImplTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyDataExtensionImplTest.java
@@ -43,8 +43,8 @@ import org.apache.sshd.common.Factory;
import org.apache.sshd.common.random.Random;
import org.apache.sshd.common.subsystem.sftp.SftpConstants;
import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.Utils;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -135,18 +135,18 @@ public class CopyDataExtensionImplTest extends AbstractSftpClientTestSupport {
private void testCopyDataExtension(byte[] data, int readOffset, int readLength, long writeOffset) throws Exception {
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
LinkOption[] options = IoUtils.getLinkOptions(true);
String baseName = readOffset + "-" + readLength + "-" + writeOffset;
Path srcFile = assertHierarchyTargetFolderExists(lclSftp, options).resolve(baseName + "-src.txt");
Files.write(srcFile, data, IoUtils.EMPTY_OPEN_OPTIONS);
- String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile);
+ String srcPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile);
Path dstFile = srcFile.getParent().resolve(baseName + "-dst.txt");
if (Files.exists(dstFile, options)) {
Files.delete(dstFile);
}
- String dstPath = Utils.resolveRelativeRemotePath(parentPath, dstFile);
+ String dstPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, dstFile);
if (writeOffset > 0L) {
Factory<? extends Random> factory = client.getRandomFactory();
Random randomizer = factory.create();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyFileExtensionImplTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyFileExtensionImplTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyFileExtensionImplTest.java
index b21da13..74d3573 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyFileExtensionImplTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyFileExtensionImplTest.java
@@ -33,7 +33,7 @@ import org.apache.sshd.client.subsystem.sftp.extensions.CopyFileExtension;
import org.apache.sshd.common.subsystem.sftp.SftpConstants;
import org.apache.sshd.common.subsystem.sftp.SftpException;
import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -56,17 +56,18 @@ public class CopyFileExtensionImplTest extends AbstractSftpClientTestSupport {
@Test
public void testCopyFileExtension() throws Exception {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(lclSftp);
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath,
+ SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(lclSftp);
byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
Path srcFile = assertHierarchyTargetFolderExists(lclSftp).resolve("src.txt");
Files.write(srcFile, data, IoUtils.EMPTY_OPEN_OPTIONS);
Path parentPath = targetPath.getParent();
- String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile);
+ String srcPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile);
Path dstFile = lclSftp.resolve("dst.txt");
- String dstPath = Utils.resolveRelativeRemotePath(parentPath, dstFile);
+ String dstPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, dstFile);
LinkOption[] options = IoUtils.getLinkOptions(true);
assertFalse("Destination file unexpectedly exists", Files.exists(dstFile, options));
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java
index af83656..a528278 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java
@@ -38,7 +38,7 @@ import org.apache.sshd.common.subsystem.sftp.extensions.SpaceAvailableExtensionI
import org.apache.sshd.server.command.Command;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystem;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -61,10 +61,10 @@ public class SpaceAvailableExtensionImplTest extends AbstractSftpClientTestSuppo
@Test
public void testFileStoreReport() throws Exception {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
Path parentPath = targetPath.getParent();
FileStore store = Files.getFileStore(lclSftp.getRoot());
- final String queryPath = Utils.resolveRelativeRemotePath(parentPath, lclSftp);
+ final String queryPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclSftp);
final SpaceAvailableExtensionInfo expected = new SpaceAvailableExtensionInfo(store);
List<NamedFactory<Command>> factories = sshd.getSubsystemFactories();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java
index 83ef70f..d778393 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java
@@ -52,7 +52,7 @@ import org.apache.sshd.server.command.Command;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystem;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -75,12 +75,12 @@ public class OpenSSHExtensionsTest extends AbstractSftpClientTestSupport {
@Test
public void testFsync() throws IOException {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
Path srcFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + ".txt");
byte[] expected = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
Path parentPath = targetPath.getParent();
- String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile);
+ String srcPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile);
try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
session.addPasswordIdentity(getCurrentTestName());
session.auth().verify(5L, TimeUnit.SECONDS);
@@ -101,11 +101,11 @@ public class OpenSSHExtensionsTest extends AbstractSftpClientTestSupport {
@Test
public void testStat() throws Exception {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
Path srcFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + ".txt");
Files.write(srcFile, (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8), IoUtils.EMPTY_OPEN_OPTIONS);
Path parentPath = targetPath.getParent();
- String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile);
+ String srcPath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, srcFile);
final AtomicReference<String> extensionHolder = new AtomicReference<>(null);
final OpenSSHStatExtensionInfo expected = new OpenSSHStatExtensionInfo();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java b/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java
index d059d36..52f7096 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java
@@ -20,7 +20,7 @@
package org.apache.sshd.common.subsystem.sftp;
import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.JUnitTestSupport;
import org.apache.sshd.util.test.NoIoTestCase;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -32,7 +32,7 @@ import org.junit.runners.MethodSorters;
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Category({ NoIoTestCase.class })
-public class SftpConstantsTest extends BaseTestSupport {
+public class SftpConstantsTest extends JUnitTestSupport {
public SftpConstantsTest() {
super();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java b/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java
index 704aa05..3075f01 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java
@@ -20,7 +20,7 @@
package org.apache.sshd.common.subsystem.sftp;
import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.JUnitTestSupport;
import org.apache.sshd.util.test.NoIoTestCase;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -32,7 +32,7 @@ import org.junit.runners.MethodSorters;
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Category({ NoIoTestCase.class })
-public class SftpUniversalOwnerAndGroupTest extends BaseTestSupport {
+public class SftpUniversalOwnerAndGroupTest extends JUnitTestSupport {
public SftpUniversalOwnerAndGroupTest() {
super();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-sftp/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java b/sshd-sftp/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
index 436c84b..da722f2 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystemFactoryTest.java
@@ -20,7 +20,7 @@
package org.apache.sshd.server.subsystem.sftp;
import org.apache.sshd.common.util.threads.CloseableExecutorService;
-import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.JUnitTestSupport;
import org.apache.sshd.util.test.NoIoTestCase;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -33,7 +33,7 @@ import org.mockito.Mockito;
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Category({ NoIoTestCase.class })
-public class SftpSubsystemFactoryTest extends BaseTestSupport {
+public class SftpSubsystemFactoryTest extends JUnitTestSupport {
public SftpSubsystemFactoryTest() {
super();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-spring-sftp/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-spring-sftp/pom.xml b/sshd-spring-sftp/pom.xml
index c5b4f56..63ec070 100644
--- a/sshd-spring-sftp/pom.xml
+++ b/sshd-spring-sftp/pom.xml
@@ -100,6 +100,13 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>test</scope>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java
----------------------------------------------------------------------
diff --git a/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java b/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java
index 1d04f60..7dc2eb0 100644
--- a/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java
+++ b/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java
@@ -37,7 +37,7 @@ import org.apache.sshd.client.subsystem.sftp.SftpClient.DirEntry;
import org.apache.sshd.client.subsystem.sftp.SftpClientFactory;
import org.apache.sshd.client.subsystem.sftp.SftpVersionSelector;
import org.apache.sshd.common.PropertyResolverUtils;
-import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
import org.apache.sshd.common.config.keys.FilePasswordProvider;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.config.keys.loader.pem.PEMResourceParserUtils;
@@ -72,7 +72,7 @@ public class ApacheSshdSftpSessionFactory
private final AtomicReference<ClientSession> sharedSessionHolder = new AtomicReference<>();
private volatile String hostValue;
- private volatile int portValue = SshConfigFileReader.DEFAULT_PORT;
+ private volatile int portValue = ConfigFileReaderSupport.DEFAULT_PORT;
private volatile String userValue;
private volatile String passwordValue;
private volatile Resource privateKey;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-spring-sftp/src/test/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactoryTest.java
----------------------------------------------------------------------
diff --git a/sshd-spring-sftp/src/test/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactoryTest.java b/sshd-spring-sftp/src/test/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactoryTest.java
index e59ebd8..75d7195 100644
--- a/sshd-spring-sftp/src/test/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactoryTest.java
+++ b/sshd-spring-sftp/src/test/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactoryTest.java
@@ -47,8 +47,9 @@ import org.apache.sshd.common.util.io.IoUtils;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.apache.sshd.util.test.JSchLogger;
-import org.apache.sshd.util.test.Utils;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -110,12 +111,12 @@ public class ApacheSshdSftpSessionFactoryTest extends BaseTestSupport {
@BeforeClass
public static void setupClientAndServer() throws Exception {
JSchLogger.init();
- sshd = Utils.setupTestServer(ApacheSshdSftpSessionFactoryTest.class);
+ sshd = CoreTestSupportUtils.setupTestServer(ApacheSshdSftpSessionFactoryTest.class);
sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
sshd.start();
port = sshd.getPort();
- client = Utils.setupTestClient(ApacheSshdSftpSessionFactoryTest.class);
+ client = CoreTestSupportUtils.setupTestClient(ApacheSshdSftpSessionFactoryTest.class);
client.start();
}
@@ -174,7 +175,7 @@ public class ApacheSshdSftpSessionFactoryTest extends BaseTestSupport {
@Test
public void testWriteRemoteFileContents() throws Exception {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
Path srcFile = Files.createDirectories(lclSftp).resolve("source.txt");
List<String> expectedLines = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
Files.deleteIfExists(srcFile);
@@ -182,7 +183,7 @@ public class ApacheSshdSftpSessionFactoryTest extends BaseTestSupport {
Path dstFile = srcFile.getParent().resolve("destination.txt");
Path parentPath = targetPath.getParent();
- String remoteFile = Utils.resolveRelativeRemotePath(parentPath, dstFile);
+ String remoteFile = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, dstFile);
SessionFactory<LsEntry> legacyFactory = getLegacySessionFactory();
SessionFactory<SftpClient.DirEntry> sshdFactory = getSshdSessionFactory();
try (Session<LsEntry> legacySession = legacyFactory.getSession();
@@ -208,14 +209,14 @@ public class ApacheSshdSftpSessionFactoryTest extends BaseTestSupport {
@Test
public void testRetrieveRemoteFileContents() throws Exception {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
Path lclFile = Files.createDirectories(lclSftp).resolve("source.txt");
List<String> expectedLines = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
Files.deleteIfExists(lclFile);
Files.write(lclFile, expectedLines, StandardCharsets.UTF_8);
Path parentPath = targetPath.getParent();
- String remoteFile = Utils.resolveRelativeRemotePath(parentPath, lclFile);
+ String remoteFile = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclFile);
SessionFactory<LsEntry> legacyFactory = getLegacySessionFactory();
SessionFactory<SftpClient.DirEntry> sshdFactory = getSshdSessionFactory();
try (Session<LsEntry> legacySession = legacyFactory.getSession();
@@ -253,8 +254,8 @@ public class ApacheSshdSftpSessionFactoryTest extends BaseTestSupport {
@Test
public void testListContents() throws Exception {
Path targetPath = detectTargetFolder();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(lclSftp); // start clean
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(lclSftp); // start clean
List<Path> subFolders = new ArrayList<>();
for (int index = 1; index <= Byte.SIZE; index++) {
@@ -272,7 +273,7 @@ public class ApacheSshdSftpSessionFactoryTest extends BaseTestSupport {
Collections.sort(subFiles, BY_CASE_INSENSITIVE_FILE_PART);
Path parentPath = targetPath.getParent();
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclSftp);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclSftp);
SessionFactory<LsEntry> legacyFactory = getLegacySessionFactory();
SessionFactory<SftpClient.DirEntry> sshdFactory = getSshdSessionFactory();
try (Session<LsEntry> legacySession = legacyFactory.getSession();
[14/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java
deleted file mode 100644
index 8b4e9ba..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java
+++ /dev/null
@@ -1,380 +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.util.io;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.SelectorUtils;
-
-/**
- * <p>Class for scanning a directory for files/directories which match certain
- * criteria.</p>
- *
- * <p>These criteria consist of selectors and patterns which have been specified.
- * With the selectors you can select which files you want to have included.
- * Files which are not selected are excluded. With patterns you can include
- * or exclude files based on their filename.</p>
- *
- * <p>The idea is simple. A given directory is recursively scanned for all files
- * and directories. Each file/directory is matched against a set of selectors,
- * including special support for matching against filenames with include and
- * and exclude patterns. Only files/directories which match at least one
- * pattern of the include pattern list or other file selector, and don't match
- * any pattern of the exclude pattern list or fail to match against a required
- * selector will be placed in the list of files/directories found.</p>
- *
- * <p>When no list of include patterns is supplied, "**" will be used, which
- * means that everything will be matched. When no list of exclude patterns is
- * supplied, an empty list is used, such that nothing will be excluded. When
- * no selectors are supplied, none are applied.</p>
- *
- * <p>The filename pattern matching is done as follows:
- * The name to be matched is split up in path segments. A path segment is the
- * name of a directory or file, which is bounded by
- * <code>File.separator</code> ('/' under UNIX, '\' under Windows).
- * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc",
- * "def","ghi" and "xyz.java".
- * The same is done for the pattern against which should be matched.</p>
- *
- * <p>The segments of the name and the pattern are then matched against each
- * other. When '**' is used for a path segment in the pattern, it matches
- * zero or more path segments of the name.</p>
- *
- * <p>There is a special case regarding the use of <code>File.separator</code>s
- * at the beginning of the pattern and the string to match:<br>
- * When a pattern starts with a <code>File.separator</code>, the string
- * to match must also start with a <code>File.separator</code>.
- * When a pattern does not start with a <code>File.separator</code>, the
- * string to match may not start with a <code>File.separator</code>.
- * When one of these rules is not obeyed, the string will not
- * match.</p>
- *
- * <p>When a name path segment is matched against a pattern path segment, the
- * following special characters can be used:<br>
- * '*' matches zero or more characters<br>
- * '?' matches one character.</p>
- *
- * <p>Examples:
- * <br>
- * <code>"**\*.class"</code> matches all <code>.class</code> files/dirs in a directory tree.
- * <br>
- * <code>"test\a??.java"</code> matches all files/dirs which start with an 'a', then two
- * more characters and then <code>".java"</code>, in a directory called test.
- * <br>
- * <code>"**"</code> matches everything in a directory tree.
- * <br>
- * <code>"**\test\**\XYZ*"</code> matches all files/dirs which start with <code>"XYZ"</code> and where
- * there is a parent directory called test (e.g. <code>"abc\test\def\ghi\XYZ123"</code>).
- * </p>
- *
- * <p>Case sensitivity may be turned off if necessary. By default, it is
- * turned on.</p>
- *
- * <p>Example of usage:</p>
- * <pre>
- * String[] includes = {"**\\*.class"};
- * String[] excludes = {"modules\\*\\**"};
- * ds.setIncludes(includes);
- * ds.setExcludes(excludes);
- * ds.setBasedir(new File("test"));
- * ds.setCaseSensitive(true);
- * ds.scan();
- *
- * System.out.println("FILES:");
- * String[] files = ds.getIncludedFiles();
- * for (int i = 0; i < files.length; i++) {
- * System.out.println(files[i]);
- * }
- * </pre>
- * <p>This will scan a directory called test for .class files, but excludes all
- * files in all proper subdirectories of a directory called "modules".</p>
- *
- * @author Arnout J. Kuiper
- * <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a>
- * @author Magesh Umasankar
- * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a>
- * @author <a href="mailto:levylambert@tiscali-dsl.de">Antoine Levy-Lambert</a>
- */
-public class DirectoryScanner {
-
- /**
- * The base directory to be scanned.
- */
- protected File basedir;
-
- /**
- * The patterns for the files to be included.
- */
- protected String[] includes;
-
- /**
- * The files which matched at least one include and no excludes
- * and were selected.
- */
- protected List<String> filesIncluded;
-
- /**
- * Whether or not the file system should be treated as a case sensitive
- * one.
- */
- protected boolean isCaseSensitive = true;
-
- public DirectoryScanner() {
- super();
- }
-
- public DirectoryScanner(String basedir, String... includes) {
- setBasedir(basedir);
- setIncludes(includes);
- }
-
- /**
- * Sets the base directory to be scanned. This is the directory which is
- * scanned recursively. All '/' and '\' characters are replaced by
- * <code>File.separatorChar</code>, so the separator used need not match
- * <code>File.separatorChar</code>.
- *
- * @param basedir The base directory to scan.
- * Must not be {@code null}.
- */
- public void setBasedir(String basedir) {
- setBasedir(new File(basedir.replace('/', File.separatorChar).replace('\\', File.separatorChar)));
- }
-
- /**
- * Sets the base directory to be scanned. This is the directory which is
- * scanned recursively.
- *
- * @param basedir The base directory for scanning.
- * Should not be {@code null}.
- */
- public void setBasedir(File basedir) {
- this.basedir = basedir;
- }
-
- /**
- * Returns the base directory to be scanned.
- * This is the directory which is scanned recursively.
- *
- * @return the base directory to be scanned
- */
- public File getBasedir() {
- return basedir;
- }
-
- /**
- * <p>Sets the list of include patterns to use. All '/' and '\' characters
- * are replaced by <code>File.separatorChar</code>, so the separator used
- * need not match <code>File.separatorChar</code>.</p>
- *
- * <p>When a pattern ends with a '/' or '\', "**" is appended.</p>
- *
- * @param includes A list of include patterns.
- * May be {@code null}, indicating that all files
- * should be included. If a non-{@code null}
- * list is given, all elements must be
- * non-{@code null}.
- */
- public void setIncludes(String[] includes) {
- if (includes == null) {
- this.includes = null;
- } else {
- this.includes = new String[includes.length];
- for (int i = 0; i < includes.length; i++) {
- this.includes[i] = normalizePattern(includes[i]);
- }
- }
- }
-
- /**
- * Scans the base directory for files which match at least one include
- * pattern and don't match any exclude patterns. If there are selectors
- * then the files must pass muster there, as well.
- *
- * @return the matching files
- * @throws IllegalStateException if the base directory was set
- * incorrectly (i.e. if it is {@code null}, doesn't exist,
- * or isn't a directory).
- */
- public String[] scan() throws IllegalStateException {
- if (basedir == null) {
- throw new IllegalStateException("No basedir set");
- }
- if (!basedir.exists()) {
- throw new IllegalStateException("basedir " + basedir
- + " does not exist");
- }
- if (!basedir.isDirectory()) {
- throw new IllegalStateException("basedir " + basedir
- + " is not a directory");
- }
- if (includes == null || includes.length == 0) {
- throw new IllegalStateException("No includes set ");
- }
-
- filesIncluded = new ArrayList<>();
-
- scandir(basedir, "");
-
- return getIncludedFiles();
- }
-
- /**
- * Scans the given directory for files and directories. Found files and
- * directories are placed in their respective collections, based on the
- * matching of includes, excludes, and the selectors. When a directory
- * is found, it is scanned recursively.
- *
- * @param dir The directory to scan. Must not be {@code null}.
- * @param vpath The path relative to the base directory (needed to
- * prevent problems with an absolute path when using
- * dir). Must not be {@code null}.
- */
- protected void scandir(File dir, String vpath) {
- String[] newfiles = dir.list();
- if (GenericUtils.isEmpty(newfiles)) {
- newfiles = GenericUtils.EMPTY_STRING_ARRAY;
- }
-
- for (String newfile : newfiles) {
- String name = vpath + newfile;
- File file = new File(dir, newfile);
- if (file.isDirectory()) {
- if (isIncluded(name)) {
- filesIncluded.add(name);
- scandir(file, name + File.separator);
- } else if (couldHoldIncluded(name)) {
- scandir(file, name + File.separator);
- }
- } else if (file.isFile()) {
- if (isIncluded(name)) {
- filesIncluded.add(name);
- }
- }
- }
- }
-
- /**
- * Returns the names of the files which matched at least one of the
- * include patterns and none of the exclude patterns.
- * The names are relative to the base directory.
- *
- * @return the names of the files which matched at least one of the
- * include patterns and none of the exclude patterns.
- */
- public String[] getIncludedFiles() {
- String[] files = new String[filesIncluded.size()];
- return filesIncluded.toArray(files);
- }
-
- /**
- * Tests whether or not a name matches against at least one include
- * pattern.
- *
- * @param name The name to match. Must not be {@code null}.
- * @return <code>true</code> when the name matches against at least one
- * include pattern, or <code>false</code> otherwise.
- */
- protected boolean isIncluded(String name) {
- for (String include : includes) {
- if (SelectorUtils.matchPath(include, name, isCaseSensitive)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Tests whether or not a name matches the start of at least one include
- * pattern.
- *
- * @param name The name to match. Must not be {@code null}.
- * @return <code>true</code> when the name matches against the start of at
- * least one include pattern, or <code>false</code> otherwise.
- */
- protected boolean couldHoldIncluded(String name) {
- for (String include : includes) {
- if (SelectorUtils.matchPatternStart(include, name, isCaseSensitive)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Normalizes the pattern, e.g. converts forward and backward slashes to the platform-specific file separator.
- *
- * @param pattern The pattern to normalize, must not be {@code null}.
- * @return The normalized pattern, never {@code null}.
- */
- private String normalizePattern(String pattern) {
- pattern = pattern.trim();
-
- if (pattern.startsWith(SelectorUtils.REGEX_HANDLER_PREFIX)) {
- if (File.separatorChar == '\\') {
- pattern = replace(pattern, "/", "\\\\", -1);
- } else {
- pattern = replace(pattern, "\\\\", "/", -1);
- }
- } else {
- pattern = pattern.replace(File.separatorChar == '/' ? '\\' : '/', File.separatorChar);
-
- if (pattern.endsWith(File.separator)) {
- pattern += "**";
- }
- }
-
- return pattern;
- }
-
- /**
- * <p>Replace a String with another String inside a larger String,
- * for the first <code>max</code> values of the search String.</p>
- *
- * <p>A {@code null} reference passed to this method is a no-op.</p>
- *
- * @param text text to search and replace in
- * @param repl String to search for
- * @param with String to replace with
- * @param max maximum number of values to replace, or <code>-1</code> if no maximum
- * @return the text with any replacements processed
- */
- @SuppressWarnings("PMD.AssignmentInOperand")
- public static String replace(String text, String repl, String with, int max) {
- if ((text == null) || (repl == null) || (with == null) || (repl.length() == 0)) {
- return text;
- }
-
- StringBuilder buf = new StringBuilder(text.length());
- int start = 0;
- for (int end = text.indexOf(repl, start); end != -1; end = text.indexOf(repl, start)) {
- buf.append(text.substring(start, end)).append(with);
- start = end + repl.length();
-
- if (--max == 0) {
- break;
- }
- }
- buf.append(text.substring(start));
- return buf.toString();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java
deleted file mode 100644
index 8a1a68d..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java
+++ /dev/null
@@ -1,66 +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.util.io;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * A {@code /dev/null} implementation - always open
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class EmptyInputStream extends InputStream {
- public static final EmptyInputStream DEV_NULL = new EmptyInputStream();
-
- public EmptyInputStream() {
- super();
- }
-
- @Override
- public int read() throws IOException {
- return -1;
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- return -1;
- }
-
- @Override
- public long skip(long n) throws IOException {
- return 0L;
- }
-
- @Override
- public int available() throws IOException {
- return 0;
- }
-
- @Override
- public synchronized void mark(int readlimit) {
- throw new UnsupportedOperationException("mark(" + readlimit + ") called despite the fact that markSupported=" + markSupported());
- }
-
- @Override
- public synchronized void reset() throws IOException {
- // ignored
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java
deleted file mode 100644
index feafd18..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java
+++ /dev/null
@@ -1,53 +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.util.io;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.nio.file.attribute.FileTime;
-import java.nio.file.attribute.PosixFilePermission;
-import java.util.Set;
-
-/**
- * @param <T> Type of information being extracted
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface FileInfoExtractor<T> {
-
- FileInfoExtractor<Boolean> EXISTS = Files::exists;
-
- FileInfoExtractor<Boolean> ISDIR = Files::isDirectory;
-
- FileInfoExtractor<Boolean> ISREG = Files::isRegularFile;
-
- FileInfoExtractor<Boolean> ISSYMLINK = (file, options) -> Files.isSymbolicLink(file);
-
- FileInfoExtractor<Long> SIZE = (file, options) -> Files.size(file);
-
- FileInfoExtractor<Set<PosixFilePermission>> PERMISSIONS = IoUtils::getPermissions;
-
- FileInfoExtractor<FileTime> LASTMODIFIED = Files::getLastModifiedTime;
-
- T infoOf(Path file, LinkOption... options) throws IOException;
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java
deleted file mode 100644
index d847079..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java
+++ /dev/null
@@ -1,32 +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.util.io;
-
-import java.io.InputStream;
-import java.nio.channels.Channel;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class InputStreamWithChannel extends InputStream implements Channel {
- protected InputStreamWithChannel() {
- super();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/IoUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/IoUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/IoUtils.java
deleted file mode 100644
index 10aa59a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/IoUtils.java
+++ /dev/null
@@ -1,556 +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.util.io;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
-import java.io.Closeable;
-import java.io.EOFException;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.CopyOption;
-import java.nio.file.FileSystem;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
-import java.nio.file.attribute.FileAttribute;
-import java.nio.file.attribute.PosixFilePermission;
-import java.nio.file.attribute.UserPrincipal;
-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.Objects;
-import java.util.Set;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.OsUtils;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class IoUtils {
-
- public static final OpenOption[] EMPTY_OPEN_OPTIONS = new OpenOption[0];
- public static final CopyOption[] EMPTY_COPY_OPTIONS = new CopyOption[0];
- public static final LinkOption[] EMPTY_LINK_OPTIONS = new LinkOption[0];
- public static final FileAttribute<?>[] EMPTY_FILE_ATTRIBUTES = new FileAttribute<?>[0];
-
- public static final List<String> WINDOWS_EXECUTABLE_EXTENSIONS = Collections.unmodifiableList(Arrays.asList(".bat", ".exe", ".cmd"));
-
- /**
- * Size of preferred work buffer when reading / writing data to / from streams
- */
- public static final int DEFAULT_COPY_SIZE = 8192;
-
- /**
- * The local O/S line separator
- */
- public static final String EOL = System.lineSeparator();
-
- /**
- * A {@link Set} of {@link StandardOpenOption}-s that indicate an intent
- * to create/modify a file
- */
- public static final Set<StandardOpenOption> WRITEABLE_OPEN_OPTIONS =
- Collections.unmodifiableSet(
- EnumSet.of(
- StandardOpenOption.APPEND, StandardOpenOption.CREATE,
- StandardOpenOption.CREATE_NEW, StandardOpenOption.DELETE_ON_CLOSE,
- StandardOpenOption.DSYNC, StandardOpenOption.SYNC,
- StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE));
-
- private static final byte[] EOL_BYTES = EOL.getBytes(StandardCharsets.UTF_8);
-
- private static final LinkOption[] NO_FOLLOW_OPTIONS = new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
-
- /**
- * Private Constructor
- */
- private IoUtils() {
- throw new UnsupportedOperationException("No instance allowed");
- }
-
- /**
- * @return The local platform line separator bytes as UTF-8. <B>Note:</B>
- * each call returns a <U>new</U> instance in order to avoid inadvertent
- * changes in shared objects
- * @see #EOL
- */
- public static byte[] getEOLBytes() {
- return EOL_BYTES.clone();
- }
-
- public static LinkOption[] getLinkOptions(boolean followLinks) {
- if (followLinks) {
- return EMPTY_LINK_OPTIONS;
- } else { // return a clone that modifications to the array will not affect others
- return NO_FOLLOW_OPTIONS.clone();
- }
- }
-
- public static long copy(InputStream source, OutputStream sink) throws IOException {
- return copy(source, sink, DEFAULT_COPY_SIZE);
- }
-
- public static long copy(InputStream source, OutputStream sink, int bufferSize) throws IOException {
- long nread = 0L;
- byte[] buf = new byte[bufferSize];
- for (int n = source.read(buf); n > 0; n = source.read(buf)) {
- sink.write(buf, 0, n);
- nread += n;
- }
-
- return nread;
- }
-
- /**
- * Closes a bunch of resources suppressing any {@link IOException}s their
- * {@link Closeable#close()} method may have thrown
- *
- * @param closeables The {@link Closeable}s to close
- * @return The <U>first</U> {@link IOException} that occurred during closing
- * of a resource - if more than one exception occurred, they are added as
- * suppressed exceptions to the first one
- * @see Throwable#getSuppressed()
- */
- @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
- public static IOException closeQuietly(Closeable... closeables) {
- IOException err = null;
- for (Closeable c : closeables) {
- try {
- if (c != null) {
- c.close();
- }
- } catch (IOException e) {
- err = GenericUtils.accumulateException(err, e);
- }
- }
-
- return err;
- }
-
- /**
- * @param fileName The file name to be evaluated - ignored if {@code null}/empty
- * @return {@code true} if the file ends in one of the {@link #WINDOWS_EXECUTABLE_EXTENSIONS}
- */
- public static boolean isWindowsExecutable(String fileName) {
- if ((fileName == null) || (fileName.length() <= 0)) {
- return false;
- }
- for (String suffix : WINDOWS_EXECUTABLE_EXTENSIONS) {
- if (fileName.endsWith(suffix)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * If the "posix" view is supported, then it returns
- * {@link Files#getPosixFilePermissions(Path, LinkOption...)}, otherwise
- * uses the {@link #getPermissionsFromFile(File)} method
- *
- * @param path The {@link Path}
- * @param options The {@link LinkOption}s to use when querying the permissions
- * @return A {@link Set} of {@link PosixFilePermission}
- * @throws IOException If failed to access the file system in order to
- * retrieve the permissions
- */
- public static Set<PosixFilePermission> getPermissions(Path path, LinkOption... options) throws IOException {
- FileSystem fs = path.getFileSystem();
- Collection<String> views = fs.supportedFileAttributeViews();
- if (views.contains("posix")) {
- return Files.getPosixFilePermissions(path, options);
- } else {
- return getPermissionsFromFile(path.toFile());
- }
- }
-
- /**
- * @param f The {@link File} to be checked
- * @return A {@link Set} of {@link PosixFilePermission}s based on whether
- * the file is readable/writable/executable. If so, then <U>all</U> the
- * relevant permissions are set (i.e., owner, group and others)
- */
- public static Set<PosixFilePermission> getPermissionsFromFile(File f) {
- Set<PosixFilePermission> perms = EnumSet.noneOf(PosixFilePermission.class);
- if (f.canRead()) {
- perms.add(PosixFilePermission.OWNER_READ);
- perms.add(PosixFilePermission.GROUP_READ);
- perms.add(PosixFilePermission.OTHERS_READ);
- }
-
- if (f.canWrite()) {
- perms.add(PosixFilePermission.OWNER_WRITE);
- perms.add(PosixFilePermission.GROUP_WRITE);
- perms.add(PosixFilePermission.OTHERS_WRITE);
- }
-
- if (isExecutable(f)) {
- perms.add(PosixFilePermission.OWNER_EXECUTE);
- perms.add(PosixFilePermission.GROUP_EXECUTE);
- perms.add(PosixFilePermission.OTHERS_EXECUTE);
- }
-
- return perms;
- }
-
- public static boolean isExecutable(File f) {
- if (f == null) {
- return false;
- }
-
- if (OsUtils.isWin32()) {
- return isWindowsExecutable(f.getName());
- } else {
- return f.canExecute();
- }
- }
-
- /**
- * If the "posix" view is supported, then it invokes
- * {@link Files#setPosixFilePermissions(Path, Set)}, otherwise
- * uses the {@link #setPermissionsToFile(File, Collection)} method
- *
- * @param path The {@link Path}
- * @param perms The {@link Set} of {@link PosixFilePermission}s
- * @throws IOException If failed to access the file system
- */
- public static void setPermissions(Path path, Set<PosixFilePermission> perms) throws IOException {
- FileSystem fs = path.getFileSystem();
- Collection<String> views = fs.supportedFileAttributeViews();
- if (views.contains("posix")) {
- Files.setPosixFilePermissions(path, perms);
- } else {
- setPermissionsToFile(path.toFile(), perms);
- }
- }
-
- /**
- * @param f The {@link File}
- * @param perms A {@link Collection} of {@link PosixFilePermission}s to set on it.
- * <B>Note:</B> the file is set to readable/writable/executable not only by the
- * owner if <U>any</U> of relevant the owner/group/others permission is set
- */
- public static void setPermissionsToFile(File f, Collection<PosixFilePermission> perms) {
- boolean readable = perms != null
- && (perms.contains(PosixFilePermission.OWNER_READ)
- || perms.contains(PosixFilePermission.GROUP_READ)
- || perms.contains(PosixFilePermission.OTHERS_READ));
- f.setReadable(readable, false);
-
- boolean writable = perms != null
- && (perms.contains(PosixFilePermission.OWNER_WRITE)
- || perms.contains(PosixFilePermission.GROUP_WRITE)
- || perms.contains(PosixFilePermission.OTHERS_WRITE));
- f.setWritable(writable, false);
-
- boolean executable = perms != null
- && (perms.contains(PosixFilePermission.OWNER_EXECUTE)
- || perms.contains(PosixFilePermission.GROUP_EXECUTE)
- || perms.contains(PosixFilePermission.OTHERS_EXECUTE));
- f.setExecutable(executable, false);
- }
-
- /**
- * <P>Get file owner.</P>
- *
- * @param path The {@link Path}
- * @param options The {@link LinkOption}s to use when querying the owner
- * @return Owner of the file or null if unsupported. <B>Note:</B> for
- * <I>Windows</I> it strips any prepended domain or group name
- * @throws IOException If failed to access the file system
- * @see Files#getOwner(Path, LinkOption...)
- */
- public static String getFileOwner(Path path, LinkOption... options) throws IOException {
- try {
- UserPrincipal principal = Files.getOwner(path, options);
- String owner = (principal == null) ? null : principal.getName();
- return OsUtils.getCanonicalUser(owner);
- } catch (UnsupportedOperationException e) {
- return null;
- }
- }
-
- /**
- * <P>Checks if a file exists - <B>Note:</B> according to the
- * <A HREF="http://docs.oracle.com/javase/tutorial/essential/io/check.html">Java tutorial - Checking a File or Directory</A>:
- * </P>
- *
- * <PRE>
- * The methods in the Path class are syntactic, meaning that they operate
- * on the Path instance. But eventually you must access the file system
- * to verify that a particular Path exists, or does not exist. You can do
- * so with the exists(Path, LinkOption...) and the notExists(Path, LinkOption...)
- * methods. Note that !Files.exists(path) is not equivalent to Files.notExists(path).
- * When you are testing a file's existence, three results are possible:
- *
- * - The file is verified to exist.
- * - The file is verified to not exist.
- * - The file's status is unknown.
- *
- * This result can occur when the program does not have access to the file.
- * If both exists and notExists return false, the existence of the file cannot
- * be verified.
- * </PRE>
- *
- * @param path The {@link Path} to be tested
- * @param options The {@link LinkOption}s to use
- * @return {@link Boolean#TRUE}/{@link Boolean#FALSE} or {@code null}
- * according to the file status as explained above
- */
- public static Boolean checkFileExists(Path path, LinkOption... options) {
- if (Files.exists(path, options)) {
- return Boolean.TRUE;
- } else if (Files.notExists(path, options)) {
- return Boolean.FALSE;
- } else {
- return null;
- }
- }
-
- /**
- * Read the requested number of bytes or fail if there are not enough left.
- *
- * @param input where to read input from
- * @param buffer destination
- * @throws IOException if there is a problem reading the file
- * @throws EOFException if the number of bytes read was incorrect
- */
- public static void readFully(InputStream input, byte[] buffer) throws IOException {
- readFully(input, buffer, 0, buffer.length);
- }
-
- /**
- * Read the requested number of bytes or fail if there are not enough left.
- *
- * @param input where to read input from
- * @param buffer destination
- * @param offset initial offset into buffer
- * @param length length to read, must be ≥ 0
- * @throws IOException if there is a problem reading the file
- * @throws EOFException if the number of bytes read was incorrect
- */
- public static void readFully(InputStream input, byte[] buffer, int offset, int length) throws IOException {
- int actual = read(input, buffer, offset, length);
- if (actual != length) {
- throw new EOFException("Premature EOF - expected=" + length + ", actual=" + actual);
- }
- }
-
- /**
- * Read as many bytes as possible until EOF or achieved required length
- *
- * @param input where to read input from
- * @param buffer destination
- * @return actual length read; may be less than requested if EOF was reached
- * @throws IOException if a read error occurs
- */
- public static int read(InputStream input, byte[] buffer) throws IOException {
- return read(input, buffer, 0, buffer.length);
- }
-
- /**
- * Read as many bytes as possible until EOF or achieved required length
- *
- * @param input where to read input from
- * @param buffer destination
- * @param offset initial offset into buffer
- * @param length length to read - ignored if non-positive
- * @return actual length read; may be less than requested if EOF was reached
- * @throws IOException if a read error occurs
- */
- public static int read(InputStream input, byte[] buffer, int offset, int length) throws IOException {
- for (int remaining = length, curOffset = offset; remaining > 0;) {
- int count = input.read(buffer, curOffset, remaining);
- if (count == -1) { // EOF before achieved required length
- return curOffset - offset;
- }
-
- remaining -= count;
- curOffset += count;
- }
-
- return length;
- }
-
- /**
- * @param perms The current {@link PosixFilePermission}s - ignored if {@code null}/empty
- * @param excluded The permissions <U>not</U> allowed to exist - ignored if {@code null}/empty
- * @return The violating {@link PosixFilePermission} - {@code null}
- * if no violating permission found
- */
- public static PosixFilePermission validateExcludedPermissions(Collection<PosixFilePermission> perms, Collection<PosixFilePermission> excluded) {
- if (GenericUtils.isEmpty(perms) || GenericUtils.isEmpty(excluded)) {
- return null;
- }
-
- for (PosixFilePermission p : excluded) {
- if (perms.contains(p)) {
- return p;
- }
- }
-
- return null;
- }
-
- /**
- * @param path The {@link Path} to check
- * @param options The {@link LinkOption}s to use when checking if path is a directory
- * @return The same input path if it is a directory
- * @throws UnsupportedOperationException if input path not a directory
- */
- public static Path ensureDirectory(Path path, LinkOption... options) {
- if (!Files.isDirectory(path, options)) {
- throw new UnsupportedOperationException("Not a directory: " + path);
- }
- return path;
- }
-
- /**
- * @param options The {@link LinkOption}s - OK if {@code null}/empty
- * @return {@code true} if the link options are {@code null}/empty or do
- * not contain {@link LinkOption#NOFOLLOW_LINKS}, {@code false} otherwise
- * (i.e., the array is not empty and contains the special value)
- */
- public static boolean followLinks(LinkOption... options) {
- if (GenericUtils.isEmpty(options)) {
- return true;
- }
-
- for (LinkOption localLinkOption : options) {
- if (localLinkOption == LinkOption.NOFOLLOW_LINKS) {
- return false;
- }
- }
- return true;
- }
-
- public static String appendPathComponent(String prefix, String component) {
- if (GenericUtils.isEmpty(prefix)) {
- return component;
- }
-
- if (GenericUtils.isEmpty(component)) {
- return prefix;
- }
-
- StringBuilder sb = new StringBuilder(prefix.length() + component.length() + File.separator.length()).append(prefix);
-
- if (sb.charAt(prefix.length() - 1) == File.separatorChar) {
- if (component.charAt(0) == File.separatorChar) {
- sb.append(component.substring(1));
- } else {
- sb.append(component);
- }
- } else {
- if (component.charAt(0) != File.separatorChar) {
- sb.append(File.separatorChar);
- }
- sb.append(component);
- }
-
- return sb.toString();
- }
-
- public static byte[] toByteArray(InputStream inStream) throws IOException {
- try (ByteArrayOutputStream baos = new ByteArrayOutputStream(DEFAULT_COPY_SIZE)) {
- copy(inStream, baos);
- return baos.toByteArray();
- }
- }
-
- /**
- * Reads all lines until no more available
- *
- * @param url The {@link URL} to read from
- * @return The {@link List} of lines in the same <U>order</U> as it was read
- * @throws IOException If failed to read the lines
- * @see #readAllLines(InputStream)
- */
- public static List<String> readAllLines(URL url) throws IOException {
- try (InputStream stream = Objects.requireNonNull(url, "No URL").openStream()) {
- return readAllLines(stream);
- }
- }
-
- /**
- * Reads all lines until no more available
- *
- * @param stream The {@link InputStream} - <B>Note:</B> assumed to
- * contain {@code UTF-8} encoded data
- * @return The {@link List} of lines in the same <U>order</U> as it was read
- * @throws IOException If failed to read the lines
- * @see #readAllLines(Reader)
- */
- public static List<String> readAllLines(InputStream stream) throws IOException {
- try (Reader reader = new InputStreamReader(Objects.requireNonNull(stream, "No stream instance"), StandardCharsets.UTF_8)) {
- return readAllLines(reader);
- }
- }
-
- public static List<String> readAllLines(Reader reader) throws IOException {
- try (BufferedReader br = new BufferedReader(Objects.requireNonNull(reader, "No reader instance"), DEFAULT_COPY_SIZE)) {
- return readAllLines(br);
- }
- }
-
- /**
- * Reads all lines until no more available
- *
- * @param reader The {@link BufferedReader} to read all lines
- * @return The {@link List} of lines in the same <U>order</U> as it was read
- * @throws IOException If failed to read the lines
- * @see #readAllLines(BufferedReader, int)
- */
- public static List<String> readAllLines(BufferedReader reader) throws IOException {
- return readAllLines(reader, -1);
- }
-
- /**
- * Reads all lines until no more available
- *
- * @param reader The {@link BufferedReader} to read all lines
- * @param lineCountHint A hint as to the expected number of lines - non-positive
- * means unknown - in which case some initial default value will be used to
- * initialize the list used to accumulate the lines.
- * @return The {@link List} of lines in the same <U>order</U> as it was read
- * @throws IOException If failed to read the lines
- */
- public static List<String> readAllLines(BufferedReader reader, int lineCountHint) throws IOException {
- List<String> result = new ArrayList<>(Math.max(lineCountHint, Short.SIZE));
- for (String line = reader.readLine(); line != null; line = reader.readLine()) {
- result.add(line);
- }
- return result;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java
deleted file mode 100644
index bbf956a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java
+++ /dev/null
@@ -1,113 +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.util.io;
-
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.channels.Channel;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Reads from another {@link InputStream} up to specified max. length
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class LimitInputStream extends FilterInputStream implements Channel {
- private final AtomicBoolean open = new AtomicBoolean(true);
- private long remaining;
-
- public LimitInputStream(InputStream in, long length) {
- super(in);
- remaining = length;
- }
-
- @Override
- public boolean isOpen() {
- return open.get();
- }
-
- @Override
- public int read() throws IOException {
- if (!isOpen()) {
- throw new IOException("read() - stream is closed (remaining=" + remaining + ")");
- }
-
- if (remaining > 0) {
- remaining--;
- return super.read();
- } else {
- return -1;
- }
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- if (!isOpen()) {
- throw new IOException("read(len=" + len + ") stream is closed (remaining=" + remaining + ")");
- }
-
- int nb = len;
- if (nb > remaining) {
- nb = (int) remaining;
- }
- if (nb > 0) {
- int read = super.read(b, off, nb);
- remaining -= read;
- return read;
- } else {
- return -1;
- }
- }
-
- @Override
- public long skip(long n) throws IOException {
- if (!isOpen()) {
- throw new IOException("skip(" + n + ") stream is closed (remaining=" + remaining + ")");
- }
-
- long skipped = super.skip(n);
- remaining -= skipped;
- return skipped;
- }
-
- @Override
- public int available() throws IOException {
- if (!isOpen()) {
- throw new IOException("available() stream is closed (remaining=" + remaining + ")");
- }
-
- int av = super.available();
- if (av > remaining) {
- return (int) remaining;
- } else {
- return av;
- }
- }
-
- @Override
- public void close() throws IOException {
- // do not close the original input stream since it serves for ACK(s)
- if (open.getAndSet(false)) {
- //noinspection UnnecessaryReturnStatement
- return; // debug breakpoint
- }
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
deleted file mode 100644
index ff4dbb6..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
+++ /dev/null
@@ -1,67 +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.util.io;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.common.PropertyResolver;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.logging.LoggingUtils;
-import org.apache.sshd.common.util.logging.SimplifiedLog;
-import org.slf4j.Logger;
-
-/**
- * Dumps everything that is written to the stream to the logger
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class LoggingFilterOutputStream extends FilterOutputStream {
-
- private final String msg;
- private final SimplifiedLog log;
- private final int chunkSize;
- private final AtomicInteger writeCount = new AtomicInteger(0);
-
- public LoggingFilterOutputStream(OutputStream out, String msg, Logger log, PropertyResolver resolver) {
- this(out, msg, log, resolver.getIntProperty(BufferUtils.HEXDUMP_CHUNK_SIZE, BufferUtils.DEFAULT_HEXDUMP_CHUNK_SIZE));
- }
-
- public LoggingFilterOutputStream(OutputStream out, String msg, Logger log, int chunkSize) {
- super(out);
- this.msg = msg;
- this.log = LoggingUtils.wrap(log);
- this.chunkSize = chunkSize;
- }
-
- @Override
- public void write(int b) throws IOException {
- byte[] d = new byte[1];
- d[0] = (byte) b;
- write(d, 0, 1);
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- int count = writeCount.incrementAndGet();
- BufferUtils.dumpHex(log, BufferUtils.DEFAULT_HEXDUMP_LEVEL, msg + "[" + count + "]", BufferUtils.DEFAULT_HEX_SEPARATOR, chunkSize, b, off, len);
- out.write(b, off, len);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java
deleted file mode 100644
index 032260b..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java
+++ /dev/null
@@ -1,258 +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.util.io;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.nio.file.attribute.FileTime;
-import java.nio.file.attribute.PosixFilePermission;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.OsUtils;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * Watches over changes for a file and re-loads them if file has changed - including
- * if file is deleted or (re-)created
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ModifiableFileWatcher extends AbstractLoggingBean {
-
- /**
- * The {@link Set} of {@link PosixFilePermission} <U>not</U> allowed if strict
- * permissions are enforced on key files
- */
- public static final Set<PosixFilePermission> STRICTLY_PROHIBITED_FILE_PERMISSION =
- Collections.unmodifiableSet(
- EnumSet.of(PosixFilePermission.GROUP_WRITE, PosixFilePermission.OTHERS_WRITE));
-
- protected final LinkOption[] options;
-
- private final Path file;
- private final AtomicBoolean lastExisted = new AtomicBoolean(false);
- private final AtomicLong lastSize = new AtomicLong(Long.MIN_VALUE);
- private final AtomicLong lastModified = new AtomicLong(-1L);
-
- public ModifiableFileWatcher(File file) {
- this(Objects.requireNonNull(file, "No file to watch").toPath());
- }
-
- public ModifiableFileWatcher(Path file) {
- this(file, IoUtils.getLinkOptions(true));
- }
-
- public ModifiableFileWatcher(Path file, LinkOption... options) {
- this.file = Objects.requireNonNull(file, "No path to watch");
- // use a clone to avoid being sensitive to changes in the passed array
- this.options = (options == null) ? IoUtils.EMPTY_LINK_OPTIONS : options.clone();
- }
-
- /**
- * @return The watched {@link Path}
- */
- public final Path getPath() {
- return file;
- }
-
- public final boolean exists() throws IOException {
- return Files.exists(getPath(), options);
- }
-
- public final long size() throws IOException {
- if (exists()) {
- return Files.size(getPath());
- } else {
- return -1L;
- }
- }
-
- public final FileTime lastModified() throws IOException {
- if (exists()) {
- BasicFileAttributes attrs = Files.readAttributes(getPath(), BasicFileAttributes.class, options);
- return attrs.lastModifiedTime();
- } else {
- return null;
- }
- }
-
- /**
- * @return {@code true} if the watched file has probably been changed
- * @throws IOException If failed to query file data
- */
- public boolean checkReloadRequired() throws IOException {
- boolean exists = exists();
- // if existence state changed from last time
- if (exists != lastExisted.getAndSet(exists)) {
- return true;
- }
-
- if (!exists) {
- // file did not exist and still does not exist
- resetReloadAttributes();
- return false;
- }
-
- long size = size();
- if (size < 0L) {
- // means file no longer exists
- resetReloadAttributes();
- return true;
- }
-
- // if size changed then obviously need reload
- if (size != lastSize.getAndSet(size)) {
- return true;
- }
-
- FileTime modifiedTime = lastModified();
- if (modifiedTime == null) {
- // means file no longer exists
- resetReloadAttributes();
- return true;
- }
-
- long timestamp = modifiedTime.toMillis();
- return timestamp != lastModified.getAndSet(timestamp);
-
- }
-
- /**
- * Resets the state attributes used to detect changes to the initial
- * construction values - i.e., file assumed not to exist and no known
- * size of modify time
- */
- public void resetReloadAttributes() {
- lastExisted.set(false);
- lastSize.set(Long.MIN_VALUE);
- lastModified.set(-1L);
- }
-
- /**
- * May be called to refresh the state attributes used to detect changes
- * e.g., file existence, size and last-modified time once re-loading is
- * successfully completed. If the file does not exist then the attributes
- * are reset to an "unknown" state.
- *
- * @throws IOException If failed to access the file (if exists)
- * @see #resetReloadAttributes()
- */
- public void updateReloadAttributes() throws IOException {
- if (exists()) {
- long size = size();
- FileTime modifiedTime = lastModified();
-
- if ((size >= 0L) && (modifiedTime != null)) {
- lastExisted.set(true);
- lastSize.set(size);
- lastModified.set(modifiedTime.toMillis());
- return;
- }
- }
-
- resetReloadAttributes();
- }
-
- @Override
- public String toString() {
- return Objects.toString(getPath());
- }
-
- /**
- * <P>Checks if a path has strict permissions</P>
- * <UL>
- *
- * <LI><P>
- * (For {@code Unix}) The path may not have group or others write permissions
- * </P></LI>
- *
- * <LI><P>
- * The path must be owned by current user.
- * </P></LI>
- *
- * <LI><P>
- * (For {@code Unix}) The path may be owned by root.
- * </P></LI>
- *
- * </UL>
- *
- * @param path The {@link Path} to be checked - ignored if {@code null}
- * or does not exist
- * @param options The {@link LinkOption}s to use to query the file's permissions
- * @return The violated permission as {@link SimpleImmutableEntry} where key
- * is a loggable message and value is the offending object
- * - e.g., {@link PosixFilePermission} or {@link String} for owner. Return
- * value is {@code null} if no violations detected
- * @throws IOException If failed to retrieve the permissions
- * @see #STRICTLY_PROHIBITED_FILE_PERMISSION
- */
- public static SimpleImmutableEntry<String, Object> validateStrictConfigFilePermissions(Path path, LinkOption... options) throws IOException {
- if ((path == null) || (!Files.exists(path, options))) {
- return null;
- }
-
- Collection<PosixFilePermission> perms = IoUtils.getPermissions(path, options);
- if (GenericUtils.isEmpty(perms)) {
- return null;
- }
-
- if (OsUtils.isUNIX()) {
- PosixFilePermission p = IoUtils.validateExcludedPermissions(perms, STRICTLY_PROHIBITED_FILE_PERMISSION);
- if (p != null) {
- return new SimpleImmutableEntry<>(String.format("Permissions violation (%s)", p), p);
- }
- }
-
- String owner = IoUtils.getFileOwner(path, options);
- if (GenericUtils.isEmpty(owner)) {
- // we cannot get owner
- // general issue: jvm does not support permissions
- // security issue: specific filesystem does not support permissions
- return null;
- }
-
- String current = OsUtils.getCurrentUser();
- Set<String> expected = new HashSet<>();
- expected.add(current);
- if (OsUtils.isUNIX()) {
- // Windows "Administrator" was considered however in Windows most likely a group is used.
- expected.add(OsUtils.ROOT_USER);
- }
-
- if (!expected.contains(owner)) {
- return new SimpleImmutableEntry<>(String.format("Owner violation (%s)", owner), owner);
- }
-
- return null;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
deleted file mode 100644
index b9aedee..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
+++ /dev/null
@@ -1,47 +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.util.io;
-
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NoCloseInputStream extends FilterInputStream {
- public NoCloseInputStream(InputStream in) {
- super(in);
- }
-
- @Override
- public void close() throws IOException {
- // ignored
- }
-
- public static InputStream resolveInputStream(InputStream input, boolean okToClose) {
- if ((input == null) || okToClose) {
- return input;
- } else {
- return new NoCloseInputStream(input);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
deleted file mode 100644
index 4ba16d3..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
+++ /dev/null
@@ -1,47 +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.util.io;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NoCloseOutputStream extends FilterOutputStream {
- public NoCloseOutputStream(OutputStream out) {
- super(out);
- }
-
- @Override
- public void close() throws IOException {
- // ignored
- }
-
- public static OutputStream resolveOutputStream(OutputStream output, boolean okToClose) {
- if ((output == null) || okToClose) {
- return output;
- } else {
- return new NoCloseOutputStream(output);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
deleted file mode 100644
index 9c8b218..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
+++ /dev/null
@@ -1,46 +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.util.io;
-
-import java.io.FilterReader;
-import java.io.IOException;
-import java.io.Reader;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NoCloseReader extends FilterReader {
- public NoCloseReader(Reader in) {
- super(in);
- }
-
- @Override
- public void close() throws IOException {
- // ignored
- }
-
- public static Reader resolveReader(Reader r, boolean okToClose) {
- if ((r == null) || okToClose) {
- return r;
- } else {
- return new NoCloseReader(r);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java
deleted file mode 100644
index 0f92697..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java
+++ /dev/null
@@ -1,46 +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.util.io;
-
-import java.io.FilterWriter;
-import java.io.IOException;
-import java.io.Writer;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NoCloseWriter extends FilterWriter {
- public NoCloseWriter(Writer out) {
- super(out);
- }
-
- @Override
- public void close() throws IOException {
- // ignored
- }
-
- public static Writer resolveWriter(Writer r, boolean okToClose) {
- if ((r == null) || okToClose) {
- return r;
- } else {
- return new NoCloseWriter(r);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java
deleted file mode 100644
index eb21383..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java
+++ /dev/null
@@ -1,90 +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.util.io;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.channels.Channel;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * A {@code /dev/null} input stream
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NullInputStream extends InputStream implements Channel {
- private final AtomicBoolean open = new AtomicBoolean(true);
-
- public NullInputStream() {
- super();
- }
-
- @Override
- public boolean isOpen() {
- return open.get();
- }
-
- @Override
- public int read() throws IOException {
- if (!isOpen()) {
- throw new EOFException("Stream is closed for reading one value");
- }
- return -1;
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- if (!isOpen()) {
- throw new EOFException("Stream is closed for reading " + len + " bytes");
- }
- return -1;
- }
-
- @Override
- public long skip(long n) throws IOException {
- if (!isOpen()) {
- throw new EOFException("Stream is closed for skipping " + n + " bytes");
- }
- return 0L;
- }
-
- @Override
- public int available() throws IOException {
- if (!isOpen()) {
- throw new EOFException("Stream is closed for availability query");
- }
- return 0;
- }
-
- @Override
- public synchronized void reset() throws IOException {
- if (!isOpen()) {
- throw new EOFException("Stream is closed for reset");
- }
- }
-
- @Override
- public void close() throws IOException {
- if (open.getAndSet(false)) {
- //noinspection UnnecessaryReturnStatement
- return; // debug breakpoint
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java
deleted file mode 100644
index 67fa2d0..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java
+++ /dev/null
@@ -1,72 +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.util.io;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.channels.Channel;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * A {code /dev/null} output stream
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class NullOutputStream extends OutputStream implements Channel {
- private final AtomicBoolean open = new AtomicBoolean(true);
-
- public NullOutputStream() {
- super();
- }
-
- @Override
- public boolean isOpen() {
- return open.get();
- }
-
- @Override
- public void write(int b) throws IOException {
- if (!isOpen()) {
- throw new EOFException("Stream is closed for writing one byte");
- }
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- if (!isOpen()) {
- throw new EOFException("Stream is closed for writing " + len + " bytes");
- }
- }
-
- @Override
- public void flush() throws IOException {
- if (!isOpen()) {
- throw new EOFException("Stream is closed for flushing");
- }
- }
-
- @Override
- public void close() throws IOException {
- if (open.getAndSet(false)) {
- //noinspection UnnecessaryReturnStatement
- return; // debug breakpoint
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java
deleted file mode 100644
index 6f81872..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java
+++ /dev/null
@@ -1,32 +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.util.io;
-
-import java.io.OutputStream;
-import java.nio.channels.Channel;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class OutputStreamWithChannel extends OutputStream implements Channel {
- protected OutputStreamWithChannel() {
- super();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Class.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Class.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Class.java
deleted file mode 100644
index b8351f1..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/ASN1Class.java
+++ /dev/null
@@ -1,93 +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.util.io.der;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum ASN1Class {
- // NOTE: order is crucial, so DON'T change it
- UNIVERSAL((byte) 0x00),
- APPLICATION((byte) 0x01),
- CONTEXT((byte) 0x02),
- PRIVATE((byte) 0x03);
-
- public static final List<ASN1Class> VALUES =
- Collections.unmodifiableList(Arrays.asList(values()));
-
- private final byte byteValue;
-
- ASN1Class(byte classValue) {
- byteValue = classValue;
- }
-
- public byte getClassValue() {
- return byteValue;
- }
-
- public static ASN1Class fromName(String s) {
- if (GenericUtils.isEmpty(s)) {
- return null;
- }
-
- for (ASN1Class c : VALUES) {
- if (s.equalsIgnoreCase(c.name())) {
- return c;
- }
- }
-
- return null;
- }
-
- /**
- * <P>The first byte in DER encoding is made of following fields</P>
- * <pre>
- *-------------------------------------------------
- *|Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
- *-------------------------------------------------
- *| Class | CF | Type |
- *-------------------------------------------------
- * </pre>
- * @param value The original DER encoded byte
- * @return The {@link ASN1Class} value - {@code null} if no match found
- * @see #fromTypeValue(int)
- */
- public static ASN1Class fromDERValue(int value) {
- return fromTypeValue((value >> 6) & 0x03);
- }
-
- /**
- * @param value The "pure" value - unshifted and with no extras
- * @return The {@link ASN1Class} value - {@code null} if no match found
- */
- public static ASN1Class fromTypeValue(int value) {
- // all 4 values are defined
- if ((value < 0) || (value >= VALUES.size())) {
- return null;
- }
-
- return VALUES.get(value);
- }
-}
[50/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolver.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolver.java
new file mode 100644
index 0000000..f1c9ea8
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolver.java
@@ -0,0 +1,107 @@
+/*
+ * 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.client.config.hosts;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.io.ModifiableFileWatcher;
+
+/**
+ * Watches for changes in a configuration file and automatically reloads any changes
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ConfigFileHostEntryResolver extends ModifiableFileWatcher implements HostConfigEntryResolver {
+ private final AtomicReference<HostConfigEntryResolver> delegateHolder = // assumes initially empty
+ new AtomicReference<>(HostConfigEntryResolver.EMPTY);
+
+ public ConfigFileHostEntryResolver(File file) {
+ this(Objects.requireNonNull(file, "No file to watch").toPath());
+ }
+
+ public ConfigFileHostEntryResolver(Path file) {
+ this(file, IoUtils.EMPTY_LINK_OPTIONS);
+ }
+
+ public ConfigFileHostEntryResolver(Path file, LinkOption... options) {
+ super(file, options);
+ }
+
+ @Override
+ public HostConfigEntry resolveEffectiveHost(String host, int port, String username) throws IOException {
+ try {
+ HostConfigEntryResolver delegate = Objects.requireNonNull(resolveEffectiveResolver(host, port, username), "No delegate");
+ HostConfigEntry entry = delegate.resolveEffectiveHost(host, port, username);
+ if (log.isDebugEnabled()) {
+ log.debug("resolveEffectiveHost({}@{}:{}) => {}", username, host, port, entry);
+ }
+
+ return entry;
+ } catch (Throwable e) {
+ if (log.isDebugEnabled()) {
+ log.debug("resolveEffectiveHost({}@{}:{}) failed ({}) to resolve: {}",
+ username, host, port, e.getClass().getSimpleName(), e.getMessage());
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("resolveEffectiveHost(" + username + "@" + host + ":" + port + ") resolution failure details", e);
+ }
+ if (e instanceof IOException) {
+ throw (IOException) e;
+ } else {
+ throw new IOException(e);
+ }
+ }
+ }
+
+ protected HostConfigEntryResolver resolveEffectiveResolver(String host, int port, String username) throws IOException {
+ if (checkReloadRequired()) {
+ delegateHolder.set(HostConfigEntryResolver.EMPTY); // start fresh
+
+ Path path = getPath();
+ if (exists()) {
+ Collection<HostConfigEntry> entries = reloadHostConfigEntries(path, host, port, username);
+ if (GenericUtils.size(entries) > 0) {
+ delegateHolder.set(HostConfigEntry.toHostConfigEntryResolver(entries));
+ }
+ } else {
+ log.info("resolveEffectiveResolver({}@{}:{}) no configuration file at {}", username, host, port, path);
+ }
+ }
+
+ return delegateHolder.get();
+ }
+
+ protected List<HostConfigEntry> reloadHostConfigEntries(Path path, String host, int port, String username) throws IOException {
+ List<HostConfigEntry> entries = HostConfigEntry.readHostConfigEntries(path);
+ log.info("resolveEffectiveResolver({}@{}:{}) loaded {} entries from {}", username, host, port, GenericUtils.size(entries), path);
+ updateReloadAttributes();
+ return entries;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
new file mode 100644
index 0000000..29bc355
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/DefaultConfigFileHostEntryResolver.java
@@ -0,0 +1,95 @@
+/*
+ * 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.client.config.hosts;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.io.IoUtils;
+
+/**
+ * Monitors the {@code ~/.ssh/config} file of the user currently running
+ * the client, re-loading it if necessary. It also (optionally) enforces
+ * the same permissions regime as {@code OpenSSH}
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DefaultConfigFileHostEntryResolver extends ConfigFileHostEntryResolver {
+ /**
+ * The default instance that enforces the same permissions regime as {@code OpenSSH}
+ */
+ public static final DefaultConfigFileHostEntryResolver INSTANCE = new DefaultConfigFileHostEntryResolver(true);
+
+ private final boolean strict;
+
+ /**
+ * @param strict If {@code true} then makes sure that the containing folder
+ * has 0700 access and the file 0644. <B>Note:</B> for <I>Windows</I> it
+ * does not check these permissions
+ * @see #validateStrictConfigFilePermissions(Path, LinkOption...)
+ */
+ public DefaultConfigFileHostEntryResolver(boolean strict) {
+ this(HostConfigEntry.getDefaultHostConfigFile(), strict);
+ }
+
+ public DefaultConfigFileHostEntryResolver(File file, boolean strict) {
+ this(Objects.requireNonNull(file, "No file provided").toPath(), strict, IoUtils.getLinkOptions(true));
+ }
+
+ public DefaultConfigFileHostEntryResolver(Path path, boolean strict, LinkOption... options) {
+ super(path, options);
+ this.strict = strict;
+ }
+
+ /**
+ * @return If {@code true} then makes sure that the containing folder
+ * has 0700 access and the file 0644. <B>Note:</B> for <I>Windows</I> it
+ * does not check these permissions
+ * @see #validateStrictConfigFilePermissions(Path, LinkOption...)
+ */
+ public final boolean isStrict() {
+ return strict;
+ }
+
+ @Override
+ protected List<HostConfigEntry> reloadHostConfigEntries(Path path, String host, int port, String username) throws IOException {
+ if (isStrict()) {
+ if (log.isDebugEnabled()) {
+ log.debug("reloadHostConfigEntries({}@{}:{}) check permissions of {}", username, host, port, path);
+ }
+
+ Map.Entry<String, ?> violation = validateStrictConfigFilePermissions(path);
+ if (violation != null) {
+ log.warn("reloadHostConfigEntries({}@{}:{}) invalid file={} permissions: {}",
+ username, host, port, path, violation.getKey());
+ updateReloadAttributes();
+ return Collections.emptyList();
+ }
+ }
+
+ return super.reloadHostConfigEntries(path, host, port, username);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
new file mode 100644
index 0000000..8a05d75
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntry.java
@@ -0,0 +1,1169 @@
+/*
+ * 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.client.config.hosts;
+
+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.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StreamCorruptedException;
+import java.io.Writer;
+import java.net.InetAddress;
+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.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.sshd.common.auth.MutableUserHolder;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
+import org.apache.sshd.common.config.keys.IdentityUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.OsUtils;
+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.NoCloseOutputStream;
+import org.apache.sshd.common.util.io.NoCloseReader;
+
+/**
+ * Represents an entry in the client's configuration file as defined by
+ * the <A HREF="http://www.gsp.com/cgi-bin/man.cgi?topic=ssh_config">configuration
+ * file format</A>
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class HostConfigEntry extends HostPatternsHolder implements MutableUserHolder {
+ /**
+ * Standard OpenSSH config file name
+ */
+ public static final String STD_CONFIG_FILENAME = "config";
+
+ public static final String HOST_CONFIG_PROP = "Host";
+ public static final String HOST_NAME_CONFIG_PROP = "HostName";
+ public static final String PORT_CONFIG_PROP = ConfigFileReaderSupport.PORT_CONFIG_PROP;
+ public static final String USER_CONFIG_PROP = "User";
+ public static final String IDENTITY_FILE_CONFIG_PROP = "IdentityFile";
+ /**
+ * Use only the identities specified in the host entry (if any)
+ */
+ public static final String EXCLUSIVE_IDENTITIES_CONFIG_PROP = "IdentitiesOnly";
+ public static final boolean DEFAULT_EXCLUSIVE_IDENTITIES = false;
+
+ /**
+ * A case <U>insensitive</U> {@link Set} of the properties that receive special handling
+ */
+ public static final Set<String> EXPLICIT_PROPERTIES =
+ Collections.unmodifiableSet(
+ GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER,
+ HOST_CONFIG_PROP, HOST_NAME_CONFIG_PROP, PORT_CONFIG_PROP,
+ USER_CONFIG_PROP, IDENTITY_FILE_CONFIG_PROP, EXCLUSIVE_IDENTITIES_CONFIG_PROP
+ ));
+
+ public static final String MULTI_VALUE_SEPARATORS = " ,";
+
+ public static final char HOME_TILDE_CHAR = '~';
+ public static final char PATH_MACRO_CHAR = '%';
+ public static final char LOCAL_HOME_MACRO = 'd';
+ public static final char LOCAL_USER_MACRO = 'u';
+ public static final char LOCAL_HOST_MACRO = 'l';
+ public static final char REMOTE_HOST_MACRO = 'h';
+ public static final char REMOTE_USER_MACRO = 'r';
+ // Extra - not part of the standard
+ public static final char REMOTE_PORT_MACRO = 'p';
+
+ private static final class LazyDefaultConfigFileHolder {
+ private static final Path CONFIG_FILE =
+ PublicKeyEntry.getDefaultKeysFolderPath().resolve(STD_CONFIG_FILENAME);
+
+ private LazyDefaultConfigFileHolder() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+ }
+
+ private String host;
+ private String hostName;
+ private int port;
+ private String username;
+ private Boolean exclusiveIdentites;
+ private Collection<String> identities = Collections.emptyList();
+ private Map<String, String> properties = Collections.emptyMap();
+
+ public HostConfigEntry() {
+ super();
+ }
+
+ public HostConfigEntry(String pattern, String host, int port, String username) {
+ setHost(pattern);
+ setHostName(host);
+ setPort(port);
+ setUsername(username);
+ }
+
+ /**
+ * @return The <U>pattern(s)</U> represented by this entry
+ */
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ setPatterns(parsePatterns(parseConfigValue(host)));
+ }
+
+ public void setHost(Collection<String> patterns) {
+ this.host = GenericUtils.join(ValidateUtils.checkNotNullAndNotEmpty(patterns, "No patterns"), ',');
+ setPatterns(parsePatterns(patterns));
+ }
+
+ /**
+ * @return The effective host name to connect to if the pattern matches
+ */
+ public String getHostName() {
+ return hostName;
+ }
+
+ public void setHostName(String hostName) {
+ this.hostName = hostName;
+ }
+
+ public String resolveHostName(String originalHost) {
+ return resolveHostName(originalHost, getHostName());
+ }
+
+ /**
+ * @return A port override - if positive
+ */
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ /**
+ * Resolves the effective port to use
+ *
+ * @param originalPort The original requested port
+ * @return If the host entry port is positive, then it is used, otherwise
+ * the original requested port
+ * @see #resolvePort(int, int)
+ */
+ public int resolvePort(int originalPort) {
+ return resolvePort(originalPort, getPort());
+ }
+
+ /**
+ * @return A username override - if not {@code null}/empty
+ */
+ @Override
+ public String getUsername() {
+ return username;
+ }
+
+ @Override
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ /**
+ * Resolves the effective username
+ *
+ * @param originalUser The original requested username
+ * @return If the configured host entry username is not {@code null}/empty
+ * then it is used, otherwise the original one.
+ * @see #resolveUsername(String)
+ */
+ public String resolveUsername(String originalUser) {
+ return resolveUsername(originalUser, getUsername());
+ }
+
+ /**
+ * @return The current identities file paths - may be {@code null}/empty
+ */
+ public Collection<String> getIdentities() {
+ return identities;
+ }
+
+ /**
+ * @param file A {@link File} that contains an identity key - never {@code null}
+ */
+ public void addIdentity(File file) {
+ addIdentity(Objects.requireNonNull(file, "No file").toPath());
+ }
+
+ /**
+ * @param path A {@link Path} to a file that contains an identity key
+ * - never {@code null}
+ */
+ public void addIdentity(Path path) {
+ addIdentity(Objects.requireNonNull(path, "No path").toAbsolutePath().normalize().toString());
+ }
+
+ /**
+ * Adds a path to an identity file
+ *
+ * @param id The identity path to add - never {@code null}
+ */
+ public void addIdentity(String id) {
+ String path = ValidateUtils.checkNotNullAndNotEmpty(id, "No identity provided");
+ if (GenericUtils.isEmpty(identities)) {
+ identities = new LinkedList<>();
+ }
+ identities.add(path);
+ }
+
+ public void setIdentities(Collection<String> identities) {
+ this.identities = (identities == null) ? Collections.emptyList() : identities;
+ }
+
+ /**
+ * @return {@code true} if must use only the identities in this entry
+ */
+ public boolean isIdentitiesOnly() {
+ return (exclusiveIdentites == null) ? DEFAULT_EXCLUSIVE_IDENTITIES : exclusiveIdentites;
+ }
+
+ public void setIdentitiesOnly(boolean identitiesOnly) {
+ exclusiveIdentites = identitiesOnly;
+ }
+
+ /**
+ * @return A {@link Map} of extra properties that have been read - may be
+ * {@code null}/empty, or even contain some values that have been parsed
+ * and set as members of the entry (e.g., host, port, etc.). <B>Note:</B>
+ * multi-valued keys use a comma-separated list of values
+ */
+ public Map<String, String> getProperties() {
+ return properties;
+ }
+
+ /**
+ * @param name Property name - never {@code null}/empty
+ * @return Property value or {@code null} if no such property
+ * @see #getProperty(String, String)
+ */
+ public String getProperty(String name) {
+ return getProperty(name, null);
+ }
+
+ /**
+ * @param name Property name - never {@code null}/empty
+ * @param defaultValue Default value to return if no such property
+ * @return The property value or the default one if no such property
+ */
+ public String getProperty(String name, String defaultValue) {
+ String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+ Map<String, String> props = getProperties();
+ if (GenericUtils.isEmpty(props)) {
+ return null;
+ }
+
+ String value = props.get(key);
+ if (GenericUtils.isEmpty(value)) {
+ return defaultValue;
+ } else {
+ return value;
+ }
+ }
+
+ /**
+ * Updates the values that are <U>not</U> already configured with those
+ * from the global entry
+ *
+ * @param globalEntry The global entry - ignored if {@code null} or
+ * same reference as this entry
+ * @return {@code true} if anything updated
+ */
+ public boolean processGlobalValues(HostConfigEntry globalEntry) {
+ if ((globalEntry == null) || (this == globalEntry)) {
+ return false;
+ }
+
+ boolean modified = false;
+ /*
+ * NOTE !!! DO NOT TRY TO CHANGE THE ORDER OF THE OR-ing AS IT
+ * WOULD CAUSE INVALID CODE EXECUTION
+ */
+ modified = updateGlobalPort(globalEntry.getPort()) || modified;
+ modified = updateGlobalHostName(globalEntry.getHostName()) || modified;
+ modified = updateGlobalUserName(globalEntry.getUsername()) || modified;
+ modified = updateGlobalIdentities(globalEntry.getIdentities()) || modified;
+ modified = updateGlobalIdentityOnly(globalEntry.isIdentitiesOnly()) || modified;
+
+ Map<String, String> updated = updateGlobalProperties(globalEntry.getProperties());
+ modified = (GenericUtils.size(updated) > 0) || modified;
+
+ return modified;
+ }
+
+ /**
+ * Sets all the properties for which no current value exists in the entry
+ *
+ * @param props The global properties - ignored if {@code null}/empty
+ * @return A {@link Map} of the <U>updated</U> properties
+ */
+ public Map<String, String> updateGlobalProperties(Map<String, String> props) {
+ if (GenericUtils.isEmpty(props)) {
+ return Collections.emptyMap();
+ }
+
+ Map<String, String> updated = null;
+ // Cannot use forEach because of the modification of the updated map value (non-final)
+ for (Map.Entry<String, String> pe : props.entrySet()) {
+ String key = pe.getKey();
+ String curValue = getProperty(key);
+ if (GenericUtils.length(curValue) > 0) {
+ continue;
+ }
+
+ String newValue = pe.getValue();
+ setProperty(key, newValue);
+
+ if (updated == null) {
+ updated = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ }
+
+ updated.put(key, newValue);
+ }
+
+ if (updated == null) {
+ return Collections.emptyMap();
+ } else {
+ return updated;
+ }
+ }
+
+ /**
+ * @param ids Global identities - ignored if {@code null}/empty or already
+ * have configured identities
+ * @return {@code true} if updated identities
+ */
+ public boolean updateGlobalIdentities(Collection<String> ids) {
+ if (GenericUtils.isEmpty(ids) || (GenericUtils.size(getIdentities()) > 0)) {
+ return false;
+ }
+
+ for (String id : ids) {
+ addIdentity(id);
+ }
+
+ return true;
+ }
+
+ /**
+ * @param user The global user name - ignored if {@code null}/empty or
+ * already have a configured user
+ * @return {@code true} if updated the username
+ */
+ public boolean updateGlobalUserName(String user) {
+ if (GenericUtils.isEmpty(user) || (GenericUtils.length(getUsername()) > 0)) {
+ return false;
+ }
+
+ setUsername(user);
+ return true;
+ }
+
+ /**
+ * @param name The global host name - ignored if {@code null}/empty or
+ * already have a configured target host
+ * @return {@code true} if updated the target host
+ */
+ public boolean updateGlobalHostName(String name) {
+ if (GenericUtils.isEmpty(name) || (GenericUtils.length(getHostName()) > 0)) {
+ return false;
+ }
+
+ setHostName(name);
+ return true;
+ }
+
+ /**
+ * @param portValue The global port value - ignored if not positive
+ * or already have a configured port
+ * @return {@code true} if updated the port value
+ */
+ public boolean updateGlobalPort(int portValue) {
+ if ((portValue <= 0) || (getPort() > 0)) {
+ return false;
+ }
+
+ setPort(portValue);
+ return true;
+ }
+
+ /**
+ * @param identitiesOnly Whether to use only the identities in this entry.
+ * Ignored if already set
+ * @return {@code true} if updated the option value
+ */
+ public boolean updateGlobalIdentityOnly(boolean identitiesOnly) {
+ if (exclusiveIdentites != null) {
+ return false;
+ }
+
+ setIdentitiesOnly(identitiesOnly);
+ return true;
+ }
+
+ /**
+ * @param name Property name - never {@code null}/empty
+ * @param valsList The available values for the property
+ * @param ignoreAlreadyInitialized If {@code false} and one of the "known"
+ * properties is encountered then throws an exception
+ * @throws IllegalArgumentException If an existing value is overwritten and
+ * <tt>ignoreAlreadyInitialized</tt> is {@code false} (except for {@link #IDENTITY_FILE_CONFIG_PROP}
+ * which is <U>cumulative</U>
+ * @see #HOST_NAME_CONFIG_PROP
+ * @see #PORT_CONFIG_PROP
+ * @see #USER_CONFIG_PROP
+ * @see #IDENTITY_FILE_CONFIG_PROP
+ */
+ public void processProperty(String name, Collection<String> valsList, boolean ignoreAlreadyInitialized) {
+ String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+ String joinedValue = GenericUtils.join(valsList, ',');
+ appendPropertyValue(key, joinedValue);
+
+ if (HOST_NAME_CONFIG_PROP.equalsIgnoreCase(key)) {
+ ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target hosts N/A: %s", joinedValue);
+
+ String curValue = getHostName();
+ ValidateUtils.checkTrue(GenericUtils.isEmpty(curValue) || ignoreAlreadyInitialized, "Already initialized %s: %s", key, curValue);
+ setHostName(joinedValue);
+ } else if (PORT_CONFIG_PROP.equalsIgnoreCase(key)) {
+ ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target ports N/A: %s", joinedValue);
+
+ int curValue = getPort();
+ ValidateUtils.checkTrue((curValue <= 0) || ignoreAlreadyInitialized, "Already initialized %s: %d", key, curValue);
+
+ int newValue = Integer.parseInt(joinedValue);
+ ValidateUtils.checkTrue(newValue > 0, "Bad new port value: %d", newValue);
+ setPort(newValue);
+ } else if (USER_CONFIG_PROP.equalsIgnoreCase(key)) {
+ ValidateUtils.checkTrue(GenericUtils.size(valsList) == 1, "Multiple target users N/A: %s", joinedValue);
+
+ String curValue = getUsername();
+ ValidateUtils.checkTrue(GenericUtils.isEmpty(curValue) || ignoreAlreadyInitialized, "Already initialized %s: %s", key, curValue);
+ setUsername(joinedValue);
+ } else if (IDENTITY_FILE_CONFIG_PROP.equalsIgnoreCase(key)) {
+ ValidateUtils.checkTrue(GenericUtils.size(valsList) > 0, "No identity files specified");
+ for (String id : valsList) {
+ addIdentity(id);
+ }
+ } else if (EXCLUSIVE_IDENTITIES_CONFIG_PROP.equalsIgnoreCase(key)) {
+ setIdentitiesOnly(
+ ConfigFileReaderSupport.parseBooleanValue(
+ ValidateUtils.checkNotNullAndNotEmpty(joinedValue, "No identities option value")));
+ }
+ }
+
+ /**
+ * Appends a value using a <U>comma</U> to an existing one. If no previous
+ * value then same as calling {@link #setProperty(String, String)}.
+ *
+ * @param name Property name - never {@code null}/empty
+ * @param value The value to be appended - ignored if {@code null}/empty
+ * @return The value <U>before</U> appending - {@code null} if no previous value
+ */
+ public String appendPropertyValue(String name, String value) {
+ String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+ String curVal = getProperty(key);
+ if (GenericUtils.isEmpty(value)) {
+ return curVal;
+ }
+
+ if (GenericUtils.isEmpty(curVal)) {
+ return setProperty(key, value);
+ }
+
+ return setProperty(key, curVal + ',' + value);
+ }
+
+ /**
+ * Sets / Replaces the property value
+ *
+ * @param name Property name - never {@code null}/empty
+ * @param value Property value - if {@code null}/empty then
+ * {@link #removeProperty(String)} is called
+ * @return The previous property value - {@code null} if no such name
+ */
+ public String setProperty(String name, String value) {
+ if (GenericUtils.isEmpty(value)) {
+ return removeProperty(name);
+ }
+
+ String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+ if (GenericUtils.isEmpty(properties)) {
+ properties = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ }
+
+ return properties.put(key, value);
+ }
+
+ /**
+ * @param name Property name - never {@code null}/empty
+ * @return The removed property value - {@code null} if no such property name
+ */
+ public String removeProperty(String name) {
+ String key = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+ Map<String, String> props = getProperties();
+ if (GenericUtils.isEmpty(props)) {
+ return null;
+ } else {
+ return props.remove(key);
+ }
+ }
+
+ /**
+ * @param properties The properties to set - if {@code null} then an empty
+ * map is effectively set. <B>Note:</B> it is highly recommended to use a
+ * <U>case insensitive</U> key mapper.
+ */
+ public void setProperties(Map<String, String> properties) {
+ this.properties = (properties == null) ? Collections.emptyMap() : properties;
+ }
+
+ public <A extends Appendable> A append(A sb) throws IOException {
+ sb.append(HOST_CONFIG_PROP).append(' ').append(ValidateUtils.checkNotNullAndNotEmpty(getHost(), "No host pattern")).append(IoUtils.EOL);
+ appendNonEmptyProperty(sb, HOST_NAME_CONFIG_PROP, getHostName());
+ appendNonEmptyPort(sb, PORT_CONFIG_PROP, getPort());
+ appendNonEmptyProperty(sb, USER_CONFIG_PROP, getUsername());
+ appendNonEmptyValues(sb, IDENTITY_FILE_CONFIG_PROP, getIdentities());
+ if (exclusiveIdentites != null) {
+ appendNonEmptyProperty(sb, EXCLUSIVE_IDENTITIES_CONFIG_PROP, ConfigFileReaderSupport.yesNoValueOf(exclusiveIdentites));
+ }
+ appendNonEmptyProperties(sb, getProperties());
+ return sb;
+ }
+
+ @Override
+ public String toString() {
+ return getHost() + ": " + getUsername() + "@" + getHostName() + ":" + getPort();
+ }
+
+ /**
+ * @param <A> The {@link Appendable} type
+ * @param sb The target appender
+ * @param name The property name - never {@code null}/empty
+ * @param port The port value - ignored if non-positive
+ * @return The target appender after having appended (or not) the value
+ * @throws IOException If failed to append the requested data
+ * @see #appendNonEmptyProperty(Appendable, String, Object)
+ */
+ public static <A extends Appendable> A appendNonEmptyPort(A sb, String name, int port) throws IOException {
+ return appendNonEmptyProperty(sb, name, (port > 0) ? Integer.toString(port) : null);
+ }
+
+ /**
+ * Appends the extra properties - while skipping the {@link #EXPLICIT_PROPERTIES} ones
+ *
+ * @param <A> The {@link Appendable} type
+ * @param sb The target appender
+ * @param props The {@link Map} of properties - ignored if {@code null}/empty
+ * @return The target appender after having appended (or not) the value
+ * @throws IOException If failed to append the requested data
+ * @see #appendNonEmptyProperty(Appendable, String, Object)
+ */
+ public static <A extends Appendable> A appendNonEmptyProperties(A sb, Map<String, ?> props) throws IOException {
+ if (GenericUtils.isEmpty(props)) {
+ return sb;
+ }
+
+ // Cannot use forEach because of the IOException being thrown by appendNonEmptyProperty
+ for (Map.Entry<String, ?> pe : props.entrySet()) {
+ String name = pe.getKey();
+ if (EXPLICIT_PROPERTIES.contains(name)) {
+ continue;
+ }
+
+ appendNonEmptyProperty(sb, name, pe.getValue());
+ }
+
+ return sb;
+ }
+
+ /**
+ * @param <A> The {@link Appendable} type
+ * @param sb The target appender
+ * @param name The property name - never {@code null}/empty
+ * @param value The property value - ignored if {@code null}. <B>Note:</B>
+ * if the string representation of the value contains any commas, they are
+ * assumed to indicate a multi-valued property which is broken down to
+ * <U>individual</U> lines - one per value.
+ * @return The target appender after having appended (or not) the value
+ * @throws IOException If failed to append the requested data
+ * @see #appendNonEmptyValues(Appendable, String, Object...)
+ */
+ public static <A extends Appendable> A appendNonEmptyProperty(A sb, String name, Object value) throws IOException {
+ String s = Objects.toString(value, null);
+ String[] vals = GenericUtils.split(s, ',');
+ return appendNonEmptyValues(sb, name, (Object[]) vals);
+ }
+
+ /**
+ * @param <A> The {@link Appendable} type
+ * @param sb The target appender
+ * @param name The property name - never {@code null}/empty
+ * @param values The values to be added - one per line - ignored if {@code null}/empty
+ * @return The target appender after having appended (or not) the value
+ * @throws IOException If failed to append the requested data
+ * @see #appendNonEmptyValues(Appendable, String, Collection)
+ */
+ public static <A extends Appendable> A appendNonEmptyValues(A sb, String name, Object... values) throws IOException {
+ return appendNonEmptyValues(sb, name, GenericUtils.isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
+ }
+
+ /**
+ * @param <A> The {@link Appendable} type
+ * @param sb The target appender
+ * @param name The property name - never {@code null}/empty
+ * @param values The values to be added - one per line - ignored if {@code null}/empty
+ * @return The target appender after having appended (or not) the value
+ * @throws IOException If failed to append the requested data
+ */
+ public static <A extends Appendable> A appendNonEmptyValues(A sb, String name, Collection<?> values) throws IOException {
+ String k = ValidateUtils.checkNotNullAndNotEmpty(name, "No property name");
+ if (GenericUtils.isEmpty(values)) {
+ return sb;
+ }
+
+ for (Object v : values) {
+ sb.append(" ").append(k).append(' ').append(Objects.toString(v)).append(IoUtils.EOL);
+ }
+
+ return sb;
+ }
+
+ /**
+ * @param entries The entries - ignored if {@code null}/empty
+ * @return A {@link HostConfigEntryResolver} wrapper using the entries
+ */
+ public static HostConfigEntryResolver toHostConfigEntryResolver(Collection<? extends HostConfigEntry> entries) {
+ if (GenericUtils.isEmpty(entries)) {
+ return HostConfigEntryResolver.EMPTY;
+ } else {
+ return (host1, port1, username1) -> {
+ List<HostConfigEntry> matches = findMatchingEntries(host1, entries);
+ int numMatches = GenericUtils.size(matches);
+ if (numMatches <= 0) {
+ return null;
+ }
+
+ HostConfigEntry match = (numMatches == 1) ? matches.get(0) : findBestMatch(matches);
+ if (match == null) {
+ ValidateUtils.throwIllegalArgumentException("No best match found for %s@%s:%d out of %d matches", username1, host1, port1, numMatches);
+ }
+
+ return normalizeEntry(match, host1, port1, username1);
+ };
+ }
+ }
+
+ /**
+ * @param entry The original entry - ignored if {@code null}
+ * @param host The original host name / address
+ * @param port The original port
+ * @param username The original user name
+ * @return A <U>cloned</U> entry whose values are resolved - including
+ * expanding macros in the identities files
+ * @throws IOException If failed to normalize the entry
+ * @see #resolveHostName(String)
+ * @see #resolvePort(int)
+ * @see #resolveUsername(String)
+ * @see #resolveIdentityFilePath(String, String, int, String)
+ */
+ public static HostConfigEntry normalizeEntry(HostConfigEntry entry, String host, int port, String username) throws IOException {
+ if (entry == null) {
+ return null;
+ }
+
+ HostConfigEntry normal = new HostConfigEntry();
+ normal.setHost(host);
+ normal.setHostName(entry.resolveHostName(host));
+ normal.setPort(entry.resolvePort(port));
+ normal.setUsername(entry.resolveUsername(username));
+
+ Map<String, String> props = entry.getProperties();
+ if (GenericUtils.size(props) > 0) {
+ normal.setProperties(new TreeMap<>(props));
+ }
+
+ Collection<String> ids = entry.getIdentities();
+ if (GenericUtils.isEmpty(ids)) {
+ return normal;
+ }
+
+ normal.setIdentities(Collections.emptyList()); // start fresh
+ for (String id : ids) {
+ String path = resolveIdentityFilePath(id, host, port, username);
+ normal.addIdentity(path);
+ }
+
+ return normal;
+ }
+
+ /**
+ * Resolves the effective target host
+ *
+ * @param originalName The original requested host
+ * @param entryName The configured host
+ * @return If the configured host entry is not {@code null}/empty
+ * then it is used, otherwise the original one.
+ */
+ public static String resolveHostName(String originalName, String entryName) {
+ if (GenericUtils.isEmpty(entryName)) {
+ return originalName;
+ } else {
+ return entryName;
+ }
+ }
+
+ /**
+ * Resolves the effective username
+ *
+ * @param originalUser The original requested username
+ * @param entryUser The configured host entry username
+ * @return If the configured host entry username is not {@code null}/empty
+ * then it is used, otherwise the original one.
+ */
+ public static String resolveUsername(String originalUser, String entryUser) {
+ if (GenericUtils.isEmpty(entryUser)) {
+ return originalUser;
+ } else {
+ return entryUser;
+ }
+ }
+
+ /**
+ * Resolves the effective port to use
+ *
+ * @param originalPort The original requested port
+ * @param entryPort The configured host entry port
+ * @return If the host entry port is positive, then it is used, otherwise
+ * the original requested port
+ */
+ public static int resolvePort(int originalPort, int entryPort) {
+ if (entryPort <= 0) {
+ return originalPort;
+ } else {
+ return entryPort;
+ }
+ }
+
+ public static List<HostConfigEntry> readHostConfigEntries(File file) throws IOException {
+ return readHostConfigEntries(file.toPath(), IoUtils.EMPTY_OPEN_OPTIONS);
+ }
+
+ public static List<HostConfigEntry> readHostConfigEntries(Path path, OpenOption... options) throws IOException {
+ try (InputStream input = Files.newInputStream(path, options)) {
+ return readHostConfigEntries(input, true);
+ }
+ }
+
+ public static List<HostConfigEntry> readHostConfigEntries(URL url) throws IOException {
+ try (InputStream input = url.openStream()) {
+ return readHostConfigEntries(input, true);
+ }
+ }
+
+ public static List<HostConfigEntry> readHostConfigEntries(String filePath) throws IOException {
+ try (InputStream inStream = new FileInputStream(filePath)) {
+ return readHostConfigEntries(inStream, true);
+ }
+ }
+
+ public static List<HostConfigEntry> readHostConfigEntries(InputStream inStream, boolean okToClose) throws IOException {
+ try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(inStream, okToClose), StandardCharsets.UTF_8)) {
+ return readHostConfigEntries(reader, true);
+ }
+ }
+
+ public static List<HostConfigEntry> readHostConfigEntries(Reader rdr, boolean okToClose) throws IOException {
+ try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(rdr, okToClose))) {
+ return readHostConfigEntries(buf);
+ }
+ }
+
+ /**
+ * Reads configuration entries
+ *
+ * @param rdr The {@link BufferedReader} to use
+ * @return The {@link List} of read {@link HostConfigEntry}-ies
+ * @throws IOException If failed to parse the read configuration
+ */
+ public static List<HostConfigEntry> readHostConfigEntries(BufferedReader rdr) throws IOException {
+ HostConfigEntry curEntry = null;
+ HostConfigEntry globalEntry = null;
+ List<HostConfigEntry> entries = null;
+
+ 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(ConfigFileReaderSupport.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 configuration value delimiter at line " + lineNumber + ": " + line);
+ }
+
+ String key = line.substring(0, pos);
+ String value = line.substring(pos + 1);
+ List<String> valsList = parseConfigValue(value);
+
+ if (HOST_CONFIG_PROP.equalsIgnoreCase(key)) {
+ if (GenericUtils.isEmpty(valsList)) {
+ throw new StreamCorruptedException("Missing host pattern(s) at line " + lineNumber + ": " + line);
+ }
+
+ // If the all-hosts pattern is used, make sure no global section already active
+ for (String name : valsList) {
+ if (ALL_HOSTS_PATTERN.equalsIgnoreCase(name) && (globalEntry != null)) {
+ throw new StreamCorruptedException("Overriding the global section with a specific one at line " + lineNumber + ": " + line);
+ }
+ }
+
+ if (curEntry != null) {
+ curEntry.processGlobalValues(globalEntry);
+ }
+
+ entries = updateEntriesList(entries, curEntry);
+
+ curEntry = new HostConfigEntry();
+ curEntry.setHost(valsList);
+ } else if (curEntry == null) {
+ // if 1st encountered property is NOT for a specific host, then configuration applies to ALL
+ curEntry = new HostConfigEntry();
+ curEntry.setHost(Collections.singletonList(ALL_HOSTS_PATTERN));
+ globalEntry = curEntry;
+ }
+
+ try {
+ curEntry.processProperty(key, valsList, false);
+ } catch (RuntimeException e) {
+ throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
+ + " to process line #" + lineNumber + " (" + line + ")"
+ + ": " + e.getMessage());
+ }
+ }
+
+ if (curEntry != null) {
+ curEntry.processGlobalValues(globalEntry);
+ }
+
+ entries = updateEntriesList(entries, curEntry);
+ if (entries == null) {
+ return Collections.emptyList();
+ } else {
+ return entries;
+ }
+ }
+
+ /**
+ * Finds the best match out of the given ones.
+ *
+ * @param matches The available matches - ignored if {@code null}/empty
+ * @return The best match or {@code null} if no matches or no best match found
+ * @see #findBestMatch(Iterator)
+ */
+ public static HostConfigEntry findBestMatch(Collection<? extends HostConfigEntry> matches) {
+ if (GenericUtils.isEmpty(matches)) {
+ return null;
+ } else {
+ return findBestMatch(matches.iterator());
+ }
+ }
+
+ /**
+ * Finds the best match out of the given ones.
+ *
+ * @param matches The available matches - ignored if {@code null}/empty
+ * @return The best match or {@code null} if no matches or no best match found
+ * @see #findBestMatch(Iterator)
+ */
+ public static HostConfigEntry findBestMatch(Iterable<? extends HostConfigEntry> matches) {
+ if (matches == null) {
+ return null;
+ } else {
+ return findBestMatch(matches.iterator());
+ }
+ }
+
+ /**
+ * Finds the best match out of the given ones. The best match is defined as one whose
+ * pattern is as <U>specific</U> as possible (if more than one match is available).
+ * I.e., a non-global match is preferred over global one, and a match with no wildcards
+ * is preferred over one with such a pattern.
+ *
+ * @param matches The available matches - ignored if {@code null}/empty
+ * @return The best match or {@code null} if no matches or no best match found
+ * @see #isSpecificHostPattern(String)
+ */
+ public static HostConfigEntry findBestMatch(Iterator<? extends HostConfigEntry> matches) {
+ if ((matches == null) || (!matches.hasNext())) {
+ return null;
+ }
+
+ HostConfigEntry candidate = matches.next();
+ int wildcardMatches = 0;
+ while (matches.hasNext()) {
+ HostConfigEntry entry = matches.next();
+ String entryPattern = entry.getHost();
+ String candidatePattern = candidate.getHost();
+ // prefer non-global entry over global entry
+ if (ALL_HOSTS_PATTERN.equalsIgnoreCase(candidatePattern)) {
+ // unlikely, but handle it
+ if (ALL_HOSTS_PATTERN.equalsIgnoreCase(entryPattern)) {
+ wildcardMatches++;
+ } else {
+ candidate = entry;
+ wildcardMatches = 0;
+ }
+ continue;
+ }
+
+ if (isSpecificHostPattern(entryPattern)) {
+ // if both are specific then no best match
+ if (isSpecificHostPattern(candidatePattern)) {
+ return null;
+ }
+
+ candidate = entry;
+ wildcardMatches = 0;
+ continue;
+ }
+
+ wildcardMatches++;
+ }
+
+ String candidatePattern = candidate.getHost();
+ // best match either has specific host or no wildcard matches
+ if ((wildcardMatches <= 0) || (isSpecificHostPattern(candidatePattern))) {
+ return candidate;
+ }
+
+ return null;
+ }
+
+ public static List<HostConfigEntry> updateEntriesList(List<HostConfigEntry> entries, HostConfigEntry curEntry) {
+ if (curEntry == null) {
+ return entries;
+ }
+
+ if (entries == null) {
+ entries = new ArrayList<>();
+ }
+
+ entries.add(curEntry);
+ return entries;
+ }
+
+ public static void writeHostConfigEntries(File file, Collection<? extends HostConfigEntry> entries) throws IOException {
+ writeHostConfigEntries(Objects.requireNonNull(file, "No file").toPath(), entries, IoUtils.EMPTY_OPEN_OPTIONS);
+ }
+
+ public static void writeHostConfigEntries(Path path, Collection<? extends HostConfigEntry> entries, OpenOption... options) throws IOException {
+ try (OutputStream outputStream = Files.newOutputStream(path, options)) {
+ writeHostConfigEntries(outputStream, true, entries);
+ }
+ }
+
+ public static void writeHostConfigEntries(OutputStream outputStream, boolean okToClose, Collection<? extends HostConfigEntry> entries) throws IOException {
+ if (GenericUtils.isEmpty(entries)) {
+ return;
+ }
+
+ try (Writer w = new OutputStreamWriter(NoCloseOutputStream.resolveOutputStream(outputStream, okToClose), StandardCharsets.UTF_8)) {
+ appendHostConfigEntries(w, entries);
+ }
+ }
+
+ public static <A extends Appendable> A appendHostConfigEntries(A sb, Collection<? extends HostConfigEntry> entries) throws IOException {
+ if (GenericUtils.isEmpty(entries)) {
+ return sb;
+ }
+
+ for (HostConfigEntry entry : entries) {
+ entry.append(sb);
+ }
+
+ return sb;
+ }
+
+ /**
+ * Checks if this is a multi-value - allow space and comma
+ *
+ * @param value The value - ignored if {@code null}/empty (after trimming)
+ * @return A {@link List} of the encountered values
+ */
+ public static List<String> parseConfigValue(String value) {
+ String s = GenericUtils.replaceWhitespaceAndTrim(value);
+ if (GenericUtils.isEmpty(s)) {
+ return Collections.emptyList();
+ }
+
+ for (int index = 0; index < MULTI_VALUE_SEPARATORS.length(); index++) {
+ char sep = MULTI_VALUE_SEPARATORS.charAt(index);
+ int pos = s.indexOf(sep);
+ if (pos >= 0) {
+ String[] vals = GenericUtils.split(s, sep);
+ if (GenericUtils.isEmpty(vals)) {
+ return Collections.emptyList();
+ } else {
+ return Arrays.asList(vals);
+ }
+ }
+ }
+
+ // this point is reached if no separators found
+ return Collections.singletonList(s);
+ }
+
+ // The file name may use the tilde syntax to refer to a user’s home directory or one of the following escape characters:
+ // '%d' (local user's home directory), '%u' (local user name), '%l' (local host name), '%h' (remote host name) or '%r' (remote user name).
+ public static String resolveIdentityFilePath(String id, String host, int port, String username) throws IOException {
+ if (GenericUtils.isEmpty(id)) {
+ return id;
+ }
+
+ String path = id.replace('/', File.separatorChar); // make sure all separators are local
+ String[] elements = GenericUtils.split(path, File.separatorChar);
+ StringBuilder sb = new StringBuilder(path.length() + Long.SIZE);
+ for (int index = 0; index < elements.length; index++) {
+ String elem = elements[index];
+ if (index > 0) {
+ sb.append(File.separatorChar);
+ }
+
+ for (int curPos = 0; curPos < elem.length(); curPos++) {
+ char ch = elem.charAt(curPos);
+ if (ch == HOME_TILDE_CHAR) {
+ ValidateUtils.checkTrue((curPos == 0) && (index == 0), "Home tilde must be first: %s", id);
+ appendUserHome(sb);
+ } else if (ch == PATH_MACRO_CHAR) {
+ curPos++;
+ ValidateUtils.checkTrue(curPos < elem.length(), "Missing macro modifier in %s", id);
+ ch = elem.charAt(curPos);
+ switch(ch) {
+ case PATH_MACRO_CHAR:
+ sb.append(ch);
+ break;
+ case LOCAL_HOME_MACRO:
+ ValidateUtils.checkTrue((curPos == 1) && (index == 0), "Home macro must be first: %s", id);
+ appendUserHome(sb);
+ break;
+ case LOCAL_USER_MACRO:
+ sb.append(ValidateUtils.checkNotNullAndNotEmpty(OsUtils.getCurrentUser(), "No local user name value"));
+ break;
+ case LOCAL_HOST_MACRO: {
+ InetAddress address = Objects.requireNonNull(InetAddress.getLocalHost(), "No local address");
+ sb.append(ValidateUtils.checkNotNullAndNotEmpty(address.getHostName(), "No local name"));
+ break;
+ }
+ case REMOTE_HOST_MACRO:
+ sb.append(ValidateUtils.checkNotNullAndNotEmpty(host, "No remote host provided"));
+ break;
+ case REMOTE_USER_MACRO:
+ sb.append(ValidateUtils.checkNotNullAndNotEmpty(username, "No remote user provided"));
+ break;
+ case REMOTE_PORT_MACRO:
+ ValidateUtils.checkTrue(port > 0, "Bad remote port value: %d", port);
+ sb.append(port);
+ break;
+ default:
+ ValidateUtils.throwIllegalArgumentException("Bad modifier '%s' in %s", String.valueOf(ch), id);
+ }
+ } else {
+ sb.append(ch);
+ }
+ }
+ }
+
+ return sb.toString();
+ }
+
+ public static StringBuilder appendUserHome(StringBuilder sb) {
+ return appendUserHome(sb, IdentityUtils.getUserHomeFolder());
+ }
+
+ public static StringBuilder appendUserHome(StringBuilder sb, Path userHome) {
+ return appendUserHome(sb, Objects.requireNonNull(userHome, "No user home folder").toString());
+ }
+
+ public static StringBuilder appendUserHome(StringBuilder sb, String userHome) {
+ if (GenericUtils.isEmpty(userHome)) {
+ return sb;
+ }
+
+ sb.append(userHome);
+ // strip any ending separator since we add our own
+ int len = sb.length();
+ if (sb.charAt(len - 1) == File.separatorChar) {
+ sb.setLength(len - 1);
+ }
+
+ return sb;
+ }
+
+ /**
+ * @return The default {@link Path} location of the OpenSSH hosts entries configuration file
+ */
+ @SuppressWarnings("synthetic-access")
+ public static Path getDefaultHostConfigFile() {
+ return LazyDefaultConfigFileHolder.CONFIG_FILE;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
new file mode 100644
index 0000000..a07cfcf
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolver.java
@@ -0,0 +1,60 @@
+/*
+ * 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.client.config.hosts;
+
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface HostConfigEntryResolver {
+
+ /**
+ * An "empty" implementation that does not resolve any entry
+ */
+ HostConfigEntryResolver EMPTY = new HostConfigEntryResolver() {
+ @Override
+ public HostConfigEntry resolveEffectiveHost(String host, int port, String username) throws IOException {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "EMPTY";
+ }
+ };
+
+ /**
+ * Invoked when creating a new client session in order to allow for overriding
+ * of the original parameters
+ *
+ * @param host The requested host - never {@code null}/empty
+ * @param port The requested port
+ * @param username The requested username
+ * @return A {@link HostConfigEntry} for the actual target - {@code null} if use
+ * original parameters. <B>Note:</B> if any identity files are attached to the
+ * configuration then they must point to <U>existing</U> locations. This means
+ * that any macros such as <code>~, %d, %h</code>, etc. must be resolved <U>prior</U>
+ * to returning the value
+ * @throws IOException If failed to resolve the configuration
+ */
+ HostConfigEntry resolveEffectiveHost(String host, int port, String username) throws IOException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
new file mode 100644
index 0000000..20d682f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostPatternValue.java
@@ -0,0 +1,97 @@
+/*
+ * 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.client.config.hosts;
+
+import java.util.regex.Pattern;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Represents a pattern definition in the <U>known_hosts</U> file
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="https://en.wikibooks.org/wiki/OpenSSH/Client_Configuration_Files#About_the_Contents_of_the_known_hosts_Files">
+ * OpenSSH cookbook - About the Contents of the known hosts Files</A>
+ */
+public class HostPatternValue {
+ private Pattern pattern;
+ private int port;
+ private boolean negated;
+
+ public HostPatternValue() {
+ super();
+ }
+
+ public HostPatternValue(Pattern pattern, boolean negated) {
+ this(pattern, 0, negated);
+ }
+
+ public HostPatternValue(Pattern pattern, int port, boolean negated) {
+ this.pattern = pattern;
+ this.port = port;
+ this.negated = negated;
+ }
+
+ public Pattern getPattern() {
+ return pattern;
+ }
+
+ public void setPattern(Pattern pattern) {
+ this.pattern = pattern;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public boolean isNegated() {
+ return negated;
+ }
+
+ public void setNegated(boolean negated) {
+ this.negated = negated;
+ }
+
+ @Override
+ public String toString() {
+ Pattern p = getPattern();
+ String purePattern = (p == null) ? null : p.pattern();
+ StringBuilder sb = new StringBuilder(GenericUtils.length(purePattern) + Short.SIZE);
+ if (isNegated()) {
+ sb.append(HostPatternsHolder.NEGATION_CHAR_PATTERN);
+ }
+
+ int portValue = getPort();
+ if (portValue > 0) {
+ sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM);
+ }
+ sb.append(purePattern);
+ if (portValue > 0) {
+ sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM);
+ sb.append(HostPatternsHolder.PORT_VALUE_DELIMITER);
+ sb.append(portValue);
+ }
+
+ return sb.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
new file mode 100644
index 0000000..9d90dac
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/HostPatternsHolder.java
@@ -0,0 +1,343 @@
+/*
+ * 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.client.config.hosts;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+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 abstract class HostPatternsHolder {
+
+ /**
+ * Used in a host pattern to denote zero or more consecutive characters
+ */
+ public static final char WILDCARD_PATTERN = '*';
+ public static final String ALL_HOSTS_PATTERN = String.valueOf(WILDCARD_PATTERN);
+
+ /**
+ * Used in a host pattern to denote any <U>one</U> character
+ */
+ public static final char SINGLE_CHAR_PATTERN = '?';
+
+ /**
+ * Used to negate a host pattern
+ */
+ public static final char NEGATION_CHAR_PATTERN = '!';
+
+ /**
+ * The available pattern characters
+ */
+ public static final String PATTERN_CHARS = new String(new char[]{WILDCARD_PATTERN, SINGLE_CHAR_PATTERN, NEGATION_CHAR_PATTERN});
+
+ /** Port value separator if non-standard port pattern used */
+ public static final char PORT_VALUE_DELIMITER = ':';
+
+ /** Non-standard port specification host pattern enclosure start delimiter */
+ public static final char NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM = '[';
+
+ /** Non-standard port specification host pattern enclosure end delimiter */
+ public static final char NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM = ']';
+
+ private Collection<HostPatternValue> patterns = new LinkedList<>();
+
+ protected HostPatternsHolder() {
+ super();
+ }
+
+ public Collection<HostPatternValue> getPatterns() {
+ return patterns;
+ }
+
+ public void setPatterns(Collection<HostPatternValue> patterns) {
+ this.patterns = patterns;
+ }
+
+ /**
+ * Checks if a given host name / address matches the entry's host pattern(s)
+ *
+ * @param host The host name / address - ignored if {@code null}/empty
+ * @param port The connection port
+ * @return {@code true} if the name / address matches the pattern(s)
+ * @see #isHostMatch(String, Pattern)
+ */
+ public boolean isHostMatch(String host, int port) {
+ return isHostMatch(host, port, getPatterns());
+ }
+
+ /**
+ * @param pattern The pattern to check - ignored if {@code null}/empty
+ * @return {@code true} if the pattern is not empty and contains no wildcard characters
+ * @see #WILDCARD_PATTERN
+ * @see #SINGLE_CHAR_PATTERN
+ * @see #SINGLE_CHAR_PATTERN
+ */
+ public static boolean isSpecificHostPattern(String pattern) {
+ if (GenericUtils.isEmpty(pattern)) {
+ return false;
+ }
+
+ for (int index = 0; index < PATTERN_CHARS.length(); index++) {
+ char ch = PATTERN_CHARS.charAt(index);
+ if (pattern.indexOf(ch) >= 0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Locates all the matching entries for a give host name / address
+ *
+ * @param host The host name / address - ignored if {@code null}/empty
+ * @param entries The {@link HostConfigEntry}-ies to scan - ignored if {@code null}/empty
+ * @return A {@link List} of all the matching entries
+ * @see #isHostMatch(String, int)
+ */
+ public static List<HostConfigEntry> findMatchingEntries(String host, HostConfigEntry... entries) {
+ // TODO in Java-8 use Stream(s) + predicate
+ if (GenericUtils.isEmpty(host) || GenericUtils.isEmpty(entries)) {
+ return Collections.emptyList();
+ } else {
+ return findMatchingEntries(host, Arrays.asList(entries));
+ }
+ }
+
+ /**
+ * Locates all the matching entries for a give host name / address
+ *
+ * @param host The host name / address - ignored if {@code null}/empty
+ * @param entries The {@link HostConfigEntry}-ies to scan - ignored if {@code null}/empty
+ * @return A {@link List} of all the matching entries
+ * @see #isHostMatch(String, int)
+ */
+ public static List<HostConfigEntry> findMatchingEntries(String host, Collection<? extends HostConfigEntry> entries) {
+ // TODO in Java-8 use Stream(s) + predicate
+ if (GenericUtils.isEmpty(host) || GenericUtils.isEmpty(entries)) {
+ return Collections.emptyList();
+ }
+
+ List<HostConfigEntry> matches = null;
+ for (HostConfigEntry entry : entries) {
+ if (!entry.isHostMatch(host, 0 /* any port */)) {
+ continue; // debug breakpoint
+ }
+
+ if (matches == null) {
+ matches = new ArrayList<>(entries.size()); // in case ALL of them match
+ }
+
+ matches.add(entry);
+ }
+
+ if (matches == null) {
+ return Collections.emptyList();
+ } else {
+ return matches;
+ }
+ }
+
+ public static boolean isHostMatch(String host, int port, Collection<HostPatternValue> patterns) {
+ if (GenericUtils.isEmpty(patterns)) {
+ return false;
+ }
+
+ boolean matchFound = false;
+ for (HostPatternValue pv : patterns) {
+ boolean negated = pv.isNegated();
+ /*
+ * If already found a match we are interested only in negations
+ */
+ if (matchFound && (!negated)) {
+ continue;
+ }
+
+ if (!isHostMatch(host, pv.getPattern())) {
+ continue;
+ }
+
+ /*
+ * According to https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5:
+ *
+ * If a negated entry is matched, then the Host entry is ignored,
+ * regardless of whether any other patterns on the line match.
+ */
+ if (negated) {
+ return false;
+ }
+
+ int pvPort = pv.getPort();
+ if ((pvPort != 0) && (port != 0) && (pvPort != port)) {
+ continue;
+ }
+
+ matchFound = true;
+ }
+
+ return matchFound;
+ }
+
+ /**
+ * Checks if a given host name / address matches a host pattern
+ *
+ * @param host The host name / address - ignored if {@code null}/empty
+ * @param pattern The host {@link Pattern} - ignored if {@code null}
+ * @return {@code true} if the name / address matches the pattern
+ */
+ public static boolean isHostMatch(String host, Pattern pattern) {
+ if (GenericUtils.isEmpty(host) || (pattern == null)) {
+ return false;
+ }
+
+ Matcher m = pattern.matcher(host);
+ return m.matches();
+ }
+
+ public static List<HostPatternValue> parsePatterns(CharSequence... patterns) {
+ return parsePatterns(GenericUtils.isEmpty(patterns) ? Collections.emptyList() : Arrays.asList(patterns));
+ }
+
+ public static List<HostPatternValue> parsePatterns(Collection<? extends CharSequence> patterns) {
+ if (GenericUtils.isEmpty(patterns)) {
+ return Collections.emptyList();
+ }
+
+ List<HostPatternValue> result = new ArrayList<>(patterns.size());
+ for (CharSequence p : patterns) {
+ result.add(ValidateUtils.checkNotNull(toPattern(p), "No pattern for %s", p));
+ }
+
+ return result;
+ }
+
+ /**
+ * Converts a host pattern string to a regular expression matcher.
+ * <B>Note:</B> pattern matching is <U>case insensitive</U>
+ *
+ * @param patternString The original pattern string - ignored if {@code null}/empty
+ * @return The regular expression matcher {@link Pattern} and the indication
+ * whether it is a negating pattern or not - {@code null} if no original string
+ * @see #NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM
+ * @see #NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM
+ * @see #WILDCARD_PATTERN
+ * @see #SINGLE_CHAR_PATTERN
+ * @see #NEGATION_CHAR_PATTERN
+ */
+ public static HostPatternValue toPattern(CharSequence patternString) {
+ String pattern = GenericUtils.replaceWhitespaceAndTrim(Objects.toString(patternString, null));
+ if (GenericUtils.isEmpty(pattern)) {
+ return null;
+ }
+
+ int patternLen = pattern.length();
+ int port = 0;
+ // Check if non-standard port value used
+ StringBuilder sb = new StringBuilder(patternLen);
+ if (pattern.charAt(0) == HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM) {
+ int pos = GenericUtils.lastIndexOf(pattern, HostPatternsHolder.PORT_VALUE_DELIMITER);
+ ValidateUtils.checkTrue(pos > 0, "Missing non-standard port value delimiter in %s", pattern);
+ ValidateUtils.checkTrue(pos < (patternLen - 1), "Missing non-standard port value number in %s", pattern);
+ ValidateUtils.checkTrue(pattern.charAt(pos - 1) == HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM,
+ "Invalid non-standard port value host pattern enclosure delimiters in %s", pattern);
+
+ String csPort = pattern.substring(pos + 1, patternLen);
+ port = Integer.parseInt(csPort);
+ ValidateUtils.checkTrue((port > 0) && (port <= 0xFFFF), "Invalid non-start port value (%d) in %s", port, pattern);
+
+ pattern = pattern.substring(1, pos - 1);
+ patternLen = pattern.length();
+ }
+
+ boolean negated = false;
+ for (int curPos = 0; curPos < patternLen; curPos++) {
+ char ch = pattern.charAt(curPos);
+ ValidateUtils.checkTrue(isValidPatternChar(ch), "Invalid host pattern char in %s", pattern);
+
+ switch(ch) {
+ case '.': // need to escape it
+ sb.append('\\').append(ch);
+ break;
+ case SINGLE_CHAR_PATTERN:
+ sb.append('.');
+ break;
+ case WILDCARD_PATTERN:
+ sb.append(".*");
+ break;
+ case NEGATION_CHAR_PATTERN:
+ ValidateUtils.checkTrue(!negated, "Double negation in %s", pattern);
+ ValidateUtils.checkTrue(curPos == 0, "Negation must be 1st char: %s", pattern);
+ negated = true;
+ break;
+ default:
+ sb.append(ch);
+ }
+ }
+
+ return new HostPatternValue(Pattern.compile(sb.toString(), Pattern.CASE_INSENSITIVE), port, negated);
+ }
+
+ /**
+ * Checks if the given character is valid for a host pattern. Valid
+ * characters are:
+ * <UL>
+ * <LI>A-Z</LI>
+ * <LI>a-z</LI>
+ * <LI>0-9</LI>
+ * <LI>Underscore (_)</LI>
+ * <LI>Hyphen (-)</LI>
+ * <LI>Dot (.)</LI>
+ * <LI>The {@link #WILDCARD_PATTERN}</LI>
+ * <LI>The {@link #SINGLE_CHAR_PATTERN}</LI>
+ * </UL>
+ *
+ * @param ch The character to validate
+ * @return {@code true} if valid pattern character
+ */
+ public static boolean isValidPatternChar(char ch) {
+ if ((ch <= ' ') || (ch >= 0x7E)) {
+ return false;
+ }
+ if ((ch >= 'a') && (ch <= 'z')) {
+ return true;
+ }
+ if ((ch >= 'A') && (ch <= 'Z')) {
+ return true;
+ }
+ if ((ch >= '0') && (ch <= '9')) {
+ return true;
+ }
+ if ("-_.".indexOf(ch) >= 0) {
+ return true;
+ }
+ return PATTERN_CHARS.indexOf(ch) >= 0;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
new file mode 100644
index 0000000..2d9a322
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostDigest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.client.config.hosts;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Objects;
+import java.util.Set;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.mac.BuiltinMacs;
+import org.apache.sshd.common.mac.Mac;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Available digesters for known hosts entries
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum KnownHostDigest implements NamedFactory<Mac> {
+ SHA1("1", BuiltinMacs.hmacsha1);
+
+ public static final Set<KnownHostDigest> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(KnownHostDigest.class));
+
+ private final String name;
+ private final Factory<Mac> factory;
+
+ KnownHostDigest(String name, Factory<Mac> factory) {
+ this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name");
+ this.factory = Objects.requireNonNull(factory, "No factory");
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public Mac create() {
+ return factory.create();
+ }
+
+ public static KnownHostDigest fromName(String name) {
+ return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
new file mode 100644
index 0000000..bcaf965
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostEntry.java
@@ -0,0 +1,276 @@
+/*
+ * 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.client.config.hosts;
+
+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.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+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;
+
+/**
+ * Contains a representation of an entry in the <code>known_hosts</code> file
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="http://www.manpagez.com/man/8/sshd/">sshd(8) man page</A>
+ */
+public class KnownHostEntry extends HostPatternsHolder {
+ /**
+ * Character that denotes that start of a marker
+ */
+ public static final char MARKER_INDICATOR = '@';
+
+ /**
+ * Standard OpenSSH config file name
+ */
+ public static final String STD_HOSTS_FILENAME = "known_hosts";
+
+ private static final class LazyDefaultConfigFileHolder {
+ private static final Path HOSTS_FILE =
+ PublicKeyEntry.getDefaultKeysFolderPath().resolve(STD_HOSTS_FILENAME);
+
+ private LazyDefaultConfigFileHolder() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+ }
+
+ private String line;
+ private String marker;
+ private AuthorizedKeyEntry keyEntry;
+ private KnownHostHashValue hashedEntry;
+
+ public KnownHostEntry() {
+ super();
+ }
+
+ /**
+ * @param line The original line from which this entry was created
+ */
+ public KnownHostEntry(String line) {
+ this.line = line;
+ }
+
+ /**
+ * @return The original line from which this entry was created
+ */
+ public String getConfigLine() {
+ return line;
+ }
+
+ public void setConfigLine(String line) {
+ this.line = line;
+ }
+
+ public String getMarker() {
+ return marker;
+ }
+
+ public void setMarker(String marker) {
+ this.marker = marker;
+ }
+
+ public AuthorizedKeyEntry getKeyEntry() {
+ return keyEntry;
+ }
+
+ public void setKeyEntry(AuthorizedKeyEntry keyEntry) {
+ this.keyEntry = keyEntry;
+ }
+
+ public KnownHostHashValue getHashedEntry() {
+ return hashedEntry;
+ }
+
+ public void setHashedEntry(KnownHostHashValue hashedEntry) {
+ this.hashedEntry = hashedEntry;
+ }
+
+ @Override
+ public boolean isHostMatch(String host, int port) {
+ if (super.isHostMatch(host, port)) {
+ return true;
+ }
+
+ KnownHostHashValue hash = getHashedEntry();
+ return (hash != null) && hash.isHostMatch(host);
+ }
+
+ @Override
+ public String toString() {
+ return getConfigLine();
+ }
+
+ /**
+ * @return The default {@link Path} location of the OpenSSH known hosts file
+ */
+ @SuppressWarnings("synthetic-access")
+ public static Path getDefaultKnownHostsFile() {
+ return LazyDefaultConfigFileHolder.HOSTS_FILE;
+ }
+
+ public static List<KnownHostEntry> readKnownHostEntries(File file) throws IOException {
+ return readKnownHostEntries(file.toPath(), IoUtils.EMPTY_OPEN_OPTIONS);
+ }
+
+ public static List<KnownHostEntry> readKnownHostEntries(Path path, OpenOption... options) throws IOException {
+ try (InputStream input = Files.newInputStream(path, options)) {
+ return readKnownHostEntries(input, true);
+ }
+ }
+
+ public static List<KnownHostEntry> readKnownHostEntries(URL url) throws IOException {
+ try (InputStream input = url.openStream()) {
+ return readKnownHostEntries(input, true);
+ }
+ }
+
+ public static List<KnownHostEntry> readKnownHostEntries(String filePath) throws IOException {
+ try (InputStream inStream = new FileInputStream(filePath)) {
+ return readKnownHostEntries(inStream, true);
+ }
+ }
+
+ public static List<KnownHostEntry> readKnownHostEntries(InputStream inStream, boolean okToClose) throws IOException {
+ try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(inStream, okToClose), StandardCharsets.UTF_8)) {
+ return readKnownHostEntries(reader, true);
+ }
+ }
+
+ public static List<KnownHostEntry> readKnownHostEntries(Reader rdr, boolean okToClose) throws IOException {
+ try (BufferedReader buf = new BufferedReader(NoCloseReader.resolveReader(rdr, okToClose))) {
+ return readKnownHostEntries(buf);
+ }
+ }
+
+ /**
+ * Reads configuration entries
+ *
+ * @param rdr The {@link BufferedReader} to use
+ * @return The {@link List} of read {@link KnownHostEntry}-ies
+ * @throws IOException If failed to parse the read configuration
+ */
+ public static List<KnownHostEntry> readKnownHostEntries(BufferedReader rdr) throws IOException {
+ List<KnownHostEntry> entries = null;
+
+ int lineNumber = 1;
+ for (String line = rdr.readLine(); line != null; line = rdr.readLine(), lineNumber++) {
+ line = GenericUtils.trimToEmpty(line);
+ if (GenericUtils.isEmpty(line)) {
+ continue;
+ }
+
+ int pos = line.indexOf(ConfigFileReaderSupport.COMMENT_CHAR);
+ if (pos == 0) {
+ continue;
+ }
+
+ if (pos > 0) {
+ line = line.substring(0, pos);
+ line = line.trim();
+ }
+
+ try {
+ KnownHostEntry entry = parseKnownHostEntry(line);
+ if (entry == null) {
+ continue;
+ }
+
+ if (entries == null) {
+ entries = new ArrayList<>();
+ }
+ entries.add(entry);
+ } catch (RuntimeException | Error e) { // TODO consider consulting a user callback
+ throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
+ + " to parse line #" + lineNumber + " '" + line + "': " + e.getMessage());
+ }
+ }
+
+ if (entries == null) {
+ return Collections.emptyList();
+ } else {
+ return entries;
+ }
+ }
+
+ public static KnownHostEntry parseKnownHostEntry(String line) {
+ return parseKnownHostEntry(GenericUtils.isEmpty(line) ? null : new KnownHostEntry(), line);
+ }
+
+ public static <E extends KnownHostEntry> E parseKnownHostEntry(E entry, String data) {
+ String line = GenericUtils.replaceWhitespaceAndTrim(data);
+ if (GenericUtils.isEmpty(line) || (line.charAt(0) == PublicKeyEntry.COMMENT_CHAR)) {
+ return entry;
+ }
+
+ entry.setConfigLine(line);
+
+ if (line.charAt(0) == MARKER_INDICATOR) {
+ int pos = line.indexOf(' ');
+ ValidateUtils.checkTrue(pos > 0, "Missing marker name end delimiter in line=%s", data);
+ ValidateUtils.checkTrue(pos > 1, "No marker name after indicator in line=%s", data);
+ entry.setMarker(line.substring(1, pos));
+ line = line.substring(pos + 1).trim();
+ } else {
+ entry.setMarker(null);
+ }
+
+ int pos = line.indexOf(' ');
+ ValidateUtils.checkTrue(pos > 0, "Missing host patterns end delimiter in line=%s", data);
+ String hostPattern = line.substring(0, pos);
+ line = line.substring(pos + 1).trim();
+
+ if (hostPattern.charAt(0) == KnownHostHashValue.HASHED_HOST_DELIMITER) {
+ KnownHostHashValue hash =
+ ValidateUtils.checkNotNull(KnownHostHashValue.parse(hostPattern),
+ "Failed to extract host hash value from line=%s", data);
+ entry.setHashedEntry(hash);
+ entry.setPatterns(null);
+ } else {
+ entry.setHashedEntry(null);
+ entry.setPatterns(parsePatterns(GenericUtils.split(hostPattern, ',')));
+ }
+
+ AuthorizedKeyEntry key =
+ ValidateUtils.checkNotNull(AuthorizedKeyEntry.parseAuthorizedKeyEntry(line),
+ "No valid key entry recovered from line=%s", data);
+ entry.setKeyEntry(key);
+ return entry;
+ }
+}
[18/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
deleted file mode 100644
index a816304..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
+++ /dev/null
@@ -1,181 +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.keyprovider;
-
-import java.security.KeyPair;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Provider for key pairs. This provider is used on the server side to provide
- * the host key, or on the client side to provide the user key.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface KeyPairProvider extends KeyIdentityProvider {
-
- /**
- * SSH identifier for RSA keys
- */
- String SSH_RSA = "ssh-rsa";
-
- /**
- * SSH identifier for DSA keys
- */
- String SSH_DSS = "ssh-dss";
-
- /**
- * SSH identifier for ED25519 elliptic curve keys
- */
- String SSH_ED25519 = "ssh-ed25519";
-
- /**
- * SSH identifier for EC keys in NIST curve P-256
- */
- String ECDSA_SHA2_NISTP256 = ECCurves.nistp256.getKeyType();
-
- /**
- * SSH identifier for EC keys in NIST curve P-384
- */
- String ECDSA_SHA2_NISTP384 = ECCurves.nistp384.getKeyType();
-
- /**
- * SSH identifier for EC keys in NIST curve P-521
- */
- String ECDSA_SHA2_NISTP521 = ECCurves.nistp521.getKeyType();
-
- /**
- * A {@link KeyPairProvider} that has no keys
- */
- KeyPairProvider EMPTY_KEYPAIR_PROVIDER =
- new KeyPairProvider() {
- @Override
- public KeyPair loadKey(String type) {
- return null;
- }
-
- @Override
- public Iterable<String> getKeyTypes() {
- return Collections.emptyList();
- }
-
- @Override
- public Iterable<KeyPair> loadKeys() {
- return Collections.emptyList();
- }
-
- @Override
- public String toString() {
- return "EMPTY_KEYPAIR_PROVIDER";
- }
- };
-
- /**
- * Load a key of the specified type which can be "ssh-rsa", "ssh-dss",
- * or "ecdsa-sha2-nistp{256,384,521}". If there is no key of this type, return
- * {@code null}
- *
- * @param type the type of key to load
- * @return a valid key pair or {@code null} if this type of key is not available
- */
- default KeyPair loadKey(String type) {
- ValidateUtils.checkNotNullAndNotEmpty(type, "No key type to load");
- return GenericUtils.stream(loadKeys())
- .filter(key -> type.equals(KeyUtils.getKeyType(key)))
- .findFirst()
- .orElse(null);
- }
-
- /**
- * @return The available {@link Iterable} key types in preferred order - never {@code null}
- */
- default Iterable<String> getKeyTypes() {
- return GenericUtils.stream(loadKeys())
- .map(KeyUtils::getKeyType)
- .filter(GenericUtils::isNotEmpty)
- .collect(Collectors.toSet());
- }
-
- /**
- * Wrap the provided {@link KeyPair}s into a {@link KeyPairProvider}
- *
- * @param pairs The available pairs - ignored if {@code null}/empty (i.e.,
- * returns {@link #EMPTY_KEYPAIR_PROVIDER})
- * @return The provider wrapper
- * @see #wrap(Iterable)
- */
- static KeyPairProvider wrap(KeyPair... pairs) {
- return GenericUtils.isEmpty(pairs) ? EMPTY_KEYPAIR_PROVIDER : wrap(Arrays.asList(pairs));
- }
-
- /**
- * Wrap the provided {@link KeyPair}s into a {@link KeyPairProvider}
- *
- * @param pairs The available pairs {@link Iterable} - ignored if {@code null} (i.e.,
- * returns {@link #EMPTY_KEYPAIR_PROVIDER})
- * @return The provider wrapper
- */
- static KeyPairProvider wrap(Iterable<KeyPair> pairs) {
- return (pairs == null) ? EMPTY_KEYPAIR_PROVIDER : new KeyPairProvider() {
- @Override
- public Iterable<KeyPair> loadKeys() {
- return pairs;
- }
-
- @Override
- public KeyPair loadKey(String type) {
- for (KeyPair kp : pairs) {
- String t = KeyUtils.getKeyType(kp);
- if (Objects.equals(type, t)) {
- return kp;
- }
- }
-
- return null;
- }
-
- @Override
- public Iterable<String> getKeyTypes() {
- // use a LinkedHashSet so as to preserve the order but avoid duplicates
- Collection<String> types = new LinkedHashSet<>();
- for (KeyPair kp : pairs) {
- String t = KeyUtils.getKeyType(kp);
- if (GenericUtils.isEmpty(t)) {
- continue; // avoid unknown key types
- }
-
- if (!types.add(t)) {
- continue; // debug breakpoint
- }
- }
-
- return types;
- }
- };
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProviderHolder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProviderHolder.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProviderHolder.java
deleted file mode 100644
index 553d553..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProviderHolder.java
+++ /dev/null
@@ -1,35 +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.keyprovider;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface KeyPairProviderHolder {
- /**
- * Retrieve the <code>KeyPairProvider</code> that will be used to find
- * the host key to use on the server side or the user key on the client side.
- *
- * @return the <code>KeyPairProvider</code>, never {@code null}
- */
- KeyPairProvider getKeyPairProvider();
-
- void setKeyPairProvider(KeyPairProvider keyPairProvider);
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
deleted file mode 100644
index bf3ec8b..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
+++ /dev/null
@@ -1,97 +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.keyprovider;
-
-import java.security.KeyPair;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.function.Function;
-
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Holds a {@link Map} of {@link String}->{@link KeyPair} where the map key
- * is the type and value is the associated {@link KeyPair}
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class MappedKeyPairProvider implements KeyPairProvider {
- /**
- * Transforms a {@link Map} of {@link String}->{@link KeyPair} to a
- * {@link KeyPairProvider} where map key is the type and value is the
- * associated {@link KeyPair}
- */
- public static final Function<Map<String, KeyPair>, KeyPairProvider> MAP_TO_KEY_PAIR_PROVIDER =
- MappedKeyPairProvider::new;
-
- private final Map<String, KeyPair> pairsMap;
-
- public MappedKeyPairProvider(KeyPair... pairs) {
- this(GenericUtils.isEmpty(pairs) ? Collections.emptyList() : Arrays.asList(pairs));
- }
-
- public MappedKeyPairProvider(Collection<? extends KeyPair> pairs) {
- this(mapUniquePairs(pairs));
- }
-
- public MappedKeyPairProvider(Map<String, KeyPair> pairsMap) {
- this.pairsMap = ValidateUtils.checkNotNullAndNotEmpty(pairsMap, "No pairs map provided");
- }
-
- @Override
- public Iterable<KeyPair> loadKeys() {
- return pairsMap.values();
- }
-
- @Override
- public KeyPair loadKey(String type) {
- return pairsMap.get(type);
- }
-
- @Override
- public Iterable<String> getKeyTypes() {
- return pairsMap.keySet();
- }
-
- @Override
- public String toString() {
- return String.valueOf(getKeyTypes());
- }
-
- public static Map<String, KeyPair> mapUniquePairs(Collection<? extends KeyPair> pairs) {
- if (GenericUtils.isEmpty(pairs)) {
- return Collections.emptyMap();
- }
-
- Map<String, KeyPair> pairsMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- for (KeyPair kp : pairs) {
- String keyType = ValidateUtils.checkNotNullAndNotEmpty(KeyUtils.getKeyType(kp), "Cannot determine key type");
- KeyPair prev = pairsMap.put(keyType, kp);
- ValidateUtils.checkTrue(prev == null, "Multiple keys of type=%s", keyType);
- }
-
- return pairsMap;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
deleted file mode 100644
index e1681d4..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
+++ /dev/null
@@ -1,111 +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.mac;
-
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Base class for <code>Mac</code> implementations based on the JCE provider.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BaseMac implements Mac {
-
- private final String algorithm;
- private final int defbsize;
- private final int bsize;
- private final byte[] tmp;
- private javax.crypto.Mac mac;
- private String s;
-
- public BaseMac(String algorithm, int bsize, int defbsize) {
- this.algorithm = algorithm;
- this.bsize = bsize;
- this.defbsize = defbsize;
- this.tmp = new byte[defbsize];
- }
-
- @Override
- public final String getAlgorithm() {
- return algorithm;
- }
-
- @Override
- public final int getBlockSize() {
- return bsize;
- }
-
- @Override
- public final int getDefaultBlockSize() {
- return defbsize;
- }
-
- @Override
- public void init(byte[] key) throws Exception {
- if (key.length > defbsize) {
- byte[] tmp = new byte[defbsize];
- System.arraycopy(key, 0, tmp, 0, defbsize);
- key = tmp;
- }
-
- SecretKeySpec skey = new SecretKeySpec(key, algorithm);
- mac = SecurityUtils.getMac(algorithm);
- mac.init(skey);
- }
-
- @Override
- public void updateUInt(long i) {
- tmp[0] = (byte) (i >>> 24);
- tmp[1] = (byte) (i >>> 16);
- tmp[2] = (byte) (i >>> 8);
- tmp[3] = (byte) i;
- update(tmp, 0, 4);
- }
-
- @Override
- public void update(byte buf[], int offset, int len) {
- mac.update(buf, offset, len);
- }
-
- @Override
- public void doFinal(byte[] buf, int offset) throws Exception {
- int blockSize = getBlockSize();
- int defaultSize = getDefaultBlockSize();
- if (blockSize != defaultSize) {
- mac.doFinal(tmp, 0);
- System.arraycopy(tmp, 0, buf, offset, blockSize);
- } else {
- mac.doFinal(buf, offset);
- }
- }
-
- @Override
- public String toString() {
- synchronized (this) {
- if (s == null) {
- s = getClass().getSimpleName() + "[" + getAlgorithm() + "] - "
- + " block=" + getBlockSize() + "/" + getDefaultBlockSize() + " bytes";
- }
- }
-
- return s;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
deleted file mode 100644
index 5a4528d..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
+++ /dev/null
@@ -1,273 +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.mac;
-
-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.TreeMap;
-
-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 macs
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum BuiltinMacs implements MacFactory {
- hmacmd5(Constants.HMAC_MD5, "HmacMD5", 16, 16),
- hmacmd596(Constants.HMAC_MD5_96, "HmacMD5", 12, 16),
- hmacsha1(Constants.HMAC_SHA1, "HmacSHA1", 20, 20),
- hmacsha196(Constants.HMAC_SHA1_96, "HmacSHA1", 12, 20),
- /** See <A HREF="https://tools.ietf.org/html/rfc6668">RFC 6668</A> */
- hmacsha256(Constants.HMAC_SHA2_256, "HmacSHA256", 32, 32),
- /** See <A HREF="https://tools.ietf.org/html/rfc6668">RFC 6668</A> */
- hmacsha512(Constants.HMAC_SHA2_512, "HmacSHA512", 64, 64);
-
- public static final Set<BuiltinMacs> VALUES =
- Collections.unmodifiableSet(EnumSet.allOf(BuiltinMacs.class));
-
- private static final Map<String, MacFactory> EXTENSIONS =
- new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
- private final String factoryName;
- private final String algorithm;
- private final int defbsize;
- private final int bsize;
-
- BuiltinMacs(String factoryName, String algorithm, int bsize, int defbsize) {
- this.factoryName = factoryName;
- this.algorithm = algorithm;
- this.bsize = bsize;
- this.defbsize = defbsize;
- }
-
- @Override
- public Mac create() {
- return new BaseMac(getAlgorithm(), getBlockSize(), getDefaultBlockSize());
- }
-
- @Override
- public final String getName() {
- return factoryName;
- }
-
- @Override
- public final String getAlgorithm() {
- return algorithm;
- }
-
- @Override
- public final int getBlockSize() {
- return bsize;
- }
-
- @Override
- public final int getDefaultBlockSize() {
- return defbsize;
- }
-
- @Override
- public final boolean isSupported() {
- return true;
- }
-
- @Override
- public final String toString() {
- return getName();
- }
-
- /**
- * 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(MacFactory 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 NavigableSet} of the currently registered extensions, sorted
- * according to the factory name (case <U>insensitive</U>)
- */
- public static NavigableSet<MacFactory> 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 MacFactory unregisterExtension(String name) {
- if (GenericUtils.isEmpty(name)) {
- return null;
- }
-
- synchronized (EXTENSIONS) {
- return EXTENSIONS.remove(name);
- }
- }
-
- /**
- * @param s The {@link Enum}'s name - ignored if {@code null}/empty
- * @return The matching {@link org.apache.sshd.common.mac.BuiltinMacs} whose {@link Enum#name()} matches
- * (case <U>insensitive</U>) the provided argument - {@code null} if no match
- */
- public static BuiltinMacs fromString(String s) {
- if (GenericUtils.isEmpty(s)) {
- return null;
- }
-
- for (BuiltinMacs c : VALUES) {
- if (s.equalsIgnoreCase(c.name())) {
- return c;
- }
- }
-
- return null;
- }
-
- /**
- * @param factory The {@link org.apache.sshd.common.NamedFactory} for the MAC - ignored if {@code null}
- * @return The matching {@link org.apache.sshd.common.mac.BuiltinMacs} whose factory name matches
- * (case <U>insensitive</U>) the digest factory name
- * @see #fromFactoryName(String)
- */
- public static BuiltinMacs fromFactory(NamedFactory<Mac> 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 BuiltinMacs} whose factory name matches
- * (case <U>insensitive</U>) the provided name - {@code null} if no match
- */
- public static BuiltinMacs fromFactoryName(String name) {
- return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
- }
-
- /**
- * @param macs A comma-separated list of MACs' 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 parseMacsList(String macs) {
- return parseMacsList(GenericUtils.split(macs, ','));
- }
-
- public static ParseResult parseMacsList(String... macs) {
- return parseMacsList(GenericUtils.isEmpty((Object[]) macs) ? Collections.emptyList() : Arrays.asList(macs));
- }
-
- public static ParseResult parseMacsList(Collection<String> macs) {
- if (GenericUtils.isEmpty(macs)) {
- return ParseResult.EMPTY;
- }
-
- List<MacFactory> factories = new ArrayList<>(macs.size());
- List<String> unknown = Collections.emptyList();
- for (String name : macs) {
- MacFactory m = resolveFactory(name);
- if (m != null) {
- factories.add(m);
- } 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 MacFactory resolveFactory(String name) {
- if (GenericUtils.isEmpty(name)) {
- return null;
- }
-
- MacFactory m = fromFactoryName(name);
- if (m != null) {
- return m;
- }
-
- synchronized (EXTENSIONS) {
- return EXTENSIONS.get(name);
- }
- }
-
- public static final class ParseResult extends NamedFactoriesListParseResult<Mac, MacFactory> {
- public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
-
- public ParseResult(List<MacFactory> parsed, List<String> unsupported) {
- super(parsed, unsupported);
- }
- }
-
- public static final class Constants {
- public static final String HMAC_MD5 = "hmac-md5";
- public static final String HMAC_MD5_96 = "hmac-md5-96";
- public static final String HMAC_SHA1 = "hmac-sha1";
- public static final String HMAC_SHA1_96 = "hmac-sha1-96";
- public static final String HMAC_SHA2_256 = "hmac-sha2-256";
- public static final String HMAC_SHA2_512 = "hmac-sha2-512";
-
- 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/mac/Mac.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/Mac.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/Mac.java
deleted file mode 100644
index 4b80447..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/Mac.java
+++ /dev/null
@@ -1,52 +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.mac;
-
-import org.apache.sshd.common.util.NumberUtils;
-
-/**
- * Message Authentication Code for use in SSH.
- * It usually wraps a javax.crypto.Mac class.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Mac extends MacInformation {
- void init(byte[] key) throws Exception;
-
- default void update(byte[] buf) {
- update(buf, 0, NumberUtils.length(buf));
- }
-
- void update(byte[] buf, int start, int len);
-
- void updateUInt(long foo);
-
- default byte[] doFinal() throws Exception {
- int blockSize = getBlockSize();
- byte[] buf = new byte[blockSize];
- doFinal(buf);
- return buf;
- }
-
- default void doFinal(byte[] buf) throws Exception {
- doFinal(buf, 0);
- }
-
- void doFinal(byte[] buf, int offset) throws Exception;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
deleted file mode 100644
index 4463600..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
+++ /dev/null
@@ -1,31 +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.mac;
-
-import org.apache.sshd.common.BuiltinFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-// CHECKSTYLE:OFF
-public interface MacFactory extends MacInformation, BuiltinFactory<Mac> {
- // nothing extra
-}
-//CHECKSTYLE:ON
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/mac/MacInformation.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/MacInformation.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/MacInformation.java
deleted file mode 100644
index 583165f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/MacInformation.java
+++ /dev/null
@@ -1,41 +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.mac;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface MacInformation {
- /**
- * @return MAC algorithm name
- */
- String getAlgorithm();
-
- /**
- * @return MAC output block size in bytes - may be less than the default
- * - e.g., MD5-96
- */
- int getBlockSize();
-
- /**
- * @return The "natural" MAC block size in bytes
- */
- int getDefaultBlockSize();
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/mac/package.html
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/package.html b/sshd-core/src/main/java/org/apache/sshd/common/mac/package.html
deleted file mode 100644
index 52a90ca..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/package.html
+++ /dev/null
@@ -1,25 +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.
--->
-<html>
-<head>
-</head>
-<body>
-
-<a href="{@docRoot}/org/apache/sshd/common/mac/Mac.html"><code>Mac</code></a> implementations.
-
-</body>
-</html>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandom.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandom.java b/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandom.java
deleted file mode 100644
index 2a1f825..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandom.java
+++ /dev/null
@@ -1,34 +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.random;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractRandom implements Random {
- protected AbstractRandom() {
- super();
- }
-
- @Override
- public String toString() {
- return getName();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandomFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandomFactory.java
deleted file mode 100644
index c1d7893..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/AbstractRandomFactory.java
+++ /dev/null
@@ -1,43 +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.random;
-
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractRandomFactory implements RandomFactory {
- private final String name;
-
- protected AbstractRandomFactory(String name) {
- this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name");
- }
-
- @Override
- public final String getName() {
- return name;
- }
-
- @Override
- public String toString() {
- return getName();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandom.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandom.java b/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandom.java
deleted file mode 100644
index ba050e6..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandom.java
+++ /dev/null
@@ -1,60 +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.random;
-
-import java.security.SecureRandom;
-
-/**
- * A <code>Random</code> implementation using the built-in {@link SecureRandom} PRNG.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class JceRandom extends AbstractRandom {
- public static final String NAME = "JCE";
-
- private byte[] tmp = new byte[16];
- private final SecureRandom random = new SecureRandom();
-
- public JceRandom() {
- super();
- }
-
- @Override
- public String getName() {
- return NAME;
- }
-
- @Override
- public synchronized void fill(byte[] foo, int start, int len) {
- if ((start == 0) && (len == foo.length)) {
- random.nextBytes(foo);
- } else {
- if (len > tmp.length) {
- tmp = new byte[len];
- }
- random.nextBytes(tmp);
- System.arraycopy(tmp, 0, foo, start, len);
- }
- }
-
- @Override
- public synchronized int random(int n) {
- return random.nextInt(n);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandomFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandomFactory.java
deleted file mode 100644
index 450a85e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandomFactory.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.random;
-
-/**
- * Named factory for the JCE <code>Random</code>
- */
-public class JceRandomFactory extends AbstractRandomFactory {
- public static final String NAME = "default";
- public static final JceRandomFactory INSTANCE = new JceRandomFactory();
-
- public JceRandomFactory() {
- super(NAME);
- }
-
- @Override
- public boolean isSupported() {
- return true;
- }
-
- @Override
- public Random create() {
- return new JceRandom();
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/random/Random.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/Random.java b/sshd-core/src/main/java/org/apache/sshd/common/random/Random.java
deleted file mode 100644
index 6e597ef..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/Random.java
+++ /dev/null
@@ -1,56 +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.random;
-
-import org.apache.sshd.common.NamedResource;
-
-/**
- * A pseudo random number generator.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Random extends NamedResource {
- /**
- * Fill the buffer with random values
- *
- * @param bytes The bytes to fill
- * @see #fill(byte[], int, int)
- */
- default void fill(byte[] bytes) {
- fill(bytes, 0, bytes.length);
- }
-
- /**
- * Fill part of bytes with random values.
- *
- * @param bytes byte array to be filled.
- * @param start index to start filling at.
- * @param len length of segment to fill.
- */
- void fill(byte[] bytes, int start, int len);
-
- /**
- * Returns a pseudo-random uniformly distributed {@code int}
- * in the half-open range [0, n).
- *
- * @param n The range upper limit
- * @return The randomly selected value in the range
- */
- int random(int n);
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/random/RandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/RandomFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/random/RandomFactory.java
deleted file mode 100644
index dded06f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/RandomFactory.java
+++ /dev/null
@@ -1,31 +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.random;
-
-import org.apache.sshd.common.BuiltinFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-// CHECKSTYLE:OFF
-public interface RandomFactory extends BuiltinFactory<Random> {
- // nothing extra
-}
-//CHECKSTYLE:ON
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java
deleted file mode 100644
index ce24112..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/SingletonRandomFactory.java
+++ /dev/null
@@ -1,70 +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.random;
-
-import java.util.Objects;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.OptionalFeature;
-
-/**
- * A random factory wrapper that uses a single random instance.
- * The underlying random instance has to be thread safe.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SingletonRandomFactory extends AbstractRandom implements RandomFactory {
-
- private final NamedFactory<Random> factory;
- private final Random random;
-
- public SingletonRandomFactory(NamedFactory<Random> factory) {
- this.factory = Objects.requireNonNull(factory, "No factory");
- this.random = Objects.requireNonNull(factory.create(), "No random instance created");
- }
-
- @Override
- public boolean isSupported() {
- if (factory instanceof OptionalFeature) {
- return ((OptionalFeature) factory).isSupported();
- } else {
- return true;
- }
- }
-
- @Override
- public void fill(byte[] bytes, int start, int len) {
- random.fill(bytes, start, len);
- }
-
- @Override
- public int random(int max) {
- return random.random(max);
- }
-
- @Override
- public String getName() {
- return factory.getName();
- }
-
- @Override
- public Random create() {
- return this;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/random/package.html
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/package.html b/sshd-core/src/main/java/org/apache/sshd/common/random/package.html
deleted file mode 100644
index 0e94d7f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/package.html
+++ /dev/null
@@ -1,25 +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.
--->
-<html>
-<head>
-</head>
-<body>
-
-<a href="{@docRoot}/org/apache/sshd/common/random/Random.html"><code>Random</code></a> implementations.
-
-</body>
-</html>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
index def98a3..f8e80c5 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
@@ -19,10 +19,12 @@
package org.apache.sshd.common.session;
import java.io.IOException;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.apache.sshd.common.AttributeStore;
import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.FactoryManagerHolder;
import org.apache.sshd.common.PropertyResolver;
import org.apache.sshd.common.Service;
@@ -318,6 +320,11 @@ public interface Session
*/
void startService(String name) throws Exception;
+ @Override
+ default <T> T resolveAttribute(AttributeKey<T> key) {
+ return resolveAttribute(this, key);
+ }
+
/**
* @param version The reported client/server version
* @return {@code true} if version not empty and starts with either
@@ -327,4 +334,24 @@ public interface Session
return GenericUtils.isNotEmpty(version)
&& (version.startsWith(DEFAULT_SSH_VERSION_PREFIX) || version.startsWith(FALLBACK_SSH_VERSION_PREFIX));
}
+
+ /**
+ * Attempts to use the session's attribute, if not found then tries the factory manager
+ *
+ * @param <T> The generic attribute type
+ * @param session The {@link Session} - ignored if {@code null}
+ * @param key The attribute key - never {@code null}
+ * @return Associated value - {@code null} if not found
+ * @see Session#getFactoryManager()
+ * @see #resolveAttribute(FactoryManager, AttributeKey)
+ */
+ static <T> T resolveAttribute(Session session, AttributeKey<T> key) {
+ Objects.requireNonNull(key, "No key");
+ if (session == null) {
+ return null;
+ }
+
+ T value = session.getAttribute(key);
+ return (value != null) ? value : FactoryManager.resolveAttribute(session.getFactoryManager(), key);
+ }
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
index 076de1e..a739c33 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
@@ -44,7 +44,6 @@ import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
-import org.apache.sshd.common.AttributeStore;
import org.apache.sshd.common.Closeable;
import org.apache.sshd.common.Factory;
import org.apache.sshd.common.FactoryManager;
@@ -2253,11 +2252,6 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
}
@Override
- public <T> T resolveAttribute(AttributeKey<T> key) {
- return AttributeStore.resolveAttribute(this, key);
- }
-
- @Override
public String getUsername() {
return username;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
deleted file mode 100644
index ef06d15..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
+++ /dev/null
@@ -1,149 +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.signature;
-
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SignatureException;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Useful base class for {@link Signature} implementation
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractSignature implements Signature {
- private java.security.Signature signatureInstance;
- private final String algorithm;
-
- protected AbstractSignature(String algorithm) {
- this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No signature algorithm specified");
- }
-
- @Override
- public final String getAlgorithm() {
- return algorithm;
- }
-
- /**
- * Initializes the internal signature instance
- *
- * @param algo The signature's algorithm
- * @param forSigning If {@code true} then it is being initialized for signing,
- * otherwise for verifying a signature
- * @return The {@link java.security.Signature} instance
- * @throws GeneralSecurityException if failed to initialize
- */
- protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
- return SecurityUtils.getSignature(algo);
- }
-
- /**
- * @return The current {@link java.security.Signature} instance
- * - {@code null} if not initialized
- * @see #doInitSignature(String, boolean)
- */
- protected java.security.Signature getSignature() {
- return signatureInstance;
- }
-
- @Override
- public byte[] sign() throws Exception {
- java.security.Signature signature = Objects.requireNonNull(getSignature(), "Signature not initialized");
- return signature.sign();
- }
-
- @Override
- public void initVerifier(PublicKey key) throws Exception {
- String algo = getAlgorithm();
- signatureInstance = Objects.requireNonNull(doInitSignature(algo, false), "No signature instance create");
- signatureInstance.initVerify(Objects.requireNonNull(key, "No public key provided"));
- }
-
- @Override
- public void initSigner(PrivateKey key) throws Exception {
- String algo = getAlgorithm();
- signatureInstance = Objects.requireNonNull(doInitSignature(algo, true), "No signature instance create");
- signatureInstance.initSign(Objects.requireNonNull(key, "No private key provided"));
- }
-
- @Override
- public void update(byte[] hash, int off, int len) throws Exception {
- java.security.Signature signature = Objects.requireNonNull(getSignature(), "Signature not initialized");
- signature.update(hash, off, len);
- }
-
- /**
- * Makes an attempt to detect if the signature is encoded or pure data
- *
- * @param sig The original signature
- * @return A {@link SimpleImmutableEntry} where first value is the key type and second
- * value is the data - {@code null} if not encoded
- */
- protected SimpleImmutableEntry<String, byte[]> extractEncodedSignature(byte[] sig) {
- final int dataLen = NumberUtils.length(sig);
- // if it is encoded then we must have at least 2 UINT32 values
- if (dataLen < (2 * Integer.BYTES)) {
- return null;
- }
-
- long keyTypeLen = BufferUtils.getUInt(sig, 0, dataLen);
- // after the key type we MUST have data bytes
- if (keyTypeLen >= (dataLen - Integer.BYTES)) {
- return null;
- }
-
- int keyTypeStartPos = Integer.BYTES;
- int keyTypeEndPos = keyTypeStartPos + (int) keyTypeLen;
- int remainLen = dataLen - keyTypeEndPos;
- // must have UINT32 with the data bytes length
- if (remainLen < Integer.BYTES) {
- return null;
- }
-
- long dataBytesLen = BufferUtils.getUInt(sig, keyTypeEndPos, remainLen);
- // make sure reported number of bytes does not exceed available
- if (dataBytesLen > (remainLen - Integer.BYTES)) {
- return null;
- }
-
- String keyType = new String(sig, keyTypeStartPos, (int) keyTypeLen, StandardCharsets.UTF_8);
- byte[] data = new byte[(int) dataBytesLen];
- System.arraycopy(sig, keyTypeEndPos + Integer.BYTES, data, 0, (int) dataBytesLen);
- return new SimpleImmutableEntry<>(keyType, data);
- }
-
- protected boolean doVerify(byte[] data) throws SignatureException {
- java.security.Signature signature = Objects.requireNonNull(getSignature(), "Signature not initialized");
- return signature.verify(data);
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + "[" + getAlgorithm() + "]";
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
deleted file mode 100644
index 7d4e1e2..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
+++ /dev/null
@@ -1,305 +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.signature;
-
-import java.security.spec.ECParameterSpec;
-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.TreeMap;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.NamedFactoriesListParseResult;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Provides easy access to the currently implemented signatures
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum BuiltinSignatures implements SignatureFactory {
- dsa(KeyPairProvider.SSH_DSS) {
- @Override
- public Signature create() {
- return new SignatureDSA();
- }
- },
- rsa(KeyPairProvider.SSH_RSA) {
- @Override
- public Signature create() {
- return new SignatureRSA();
- }
- },
- nistp256(KeyPairProvider.ECDSA_SHA2_NISTP256) {
- @Override
- public Signature create() {
- return new SignatureECDSA.SignatureECDSA256();
- }
-
- @Override
- public boolean isSupported() {
- return SecurityUtils.isECCSupported();
- }
- },
- nistp384(KeyPairProvider.ECDSA_SHA2_NISTP384) {
- @Override
- public Signature create() {
- return new SignatureECDSA.SignatureECDSA384();
- }
-
- @Override
- public boolean isSupported() {
- return SecurityUtils.isECCSupported();
- }
- },
- nistp521(KeyPairProvider.ECDSA_SHA2_NISTP521) {
- @Override
- public Signature create() {
- return new SignatureECDSA.SignatureECDSA521();
- }
-
- @Override
- public boolean isSupported() {
- return SecurityUtils.isECCSupported();
- }
- },
- ed25519(KeyPairProvider.SSH_ED25519) {
- @Override
- public Signature create() {
- return SecurityUtils.getEDDSASigner();
- }
-
- @Override
- public boolean isSupported() {
- return SecurityUtils.isEDDSACurveSupported();
- }
- };
-
- public static final Set<BuiltinSignatures> VALUES =
- Collections.unmodifiableSet(EnumSet.allOf(BuiltinSignatures.class));
-
- private static final Map<String, SignatureFactory> EXTENSIONS =
- new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
- private final String factoryName;
-
- BuiltinSignatures(String facName) {
- factoryName = facName;
- }
-
- public static Signature getByCurveSize(ECParameterSpec params) {
- int curveSize = ECCurves.getCurveSize(params);
- if (curveSize <= 256) {
- return nistp256.create();
- } else if (curveSize <= 384) {
- return nistp384.create();
- } else {
- return nistp521.create();
- }
- }
-
- @Override
- public final String getName() {
- return factoryName;
- }
-
- @Override
- public final String toString() {
- return getName();
- }
-
- @Override
- public boolean isSupported() {
- return true;
- }
-
- /**
- * 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(SignatureFactory 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 NavigableSet} of the currently registered extensions, sorted
- * according to the factory name (case <U>insensitive</U>)
- */
- public static NavigableSet<SignatureFactory> 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 SignatureFactory unregisterExtension(String name) {
- if (GenericUtils.isEmpty(name)) {
- return null;
- }
-
- synchronized (EXTENSIONS) {
- return EXTENSIONS.remove(name);
- }
- }
-
- /**
- * @param s The {@link Enum}'s name - ignored if {@code null}/empty
- * @return The matching {@link org.apache.sshd.common.signature.BuiltinSignatures} whose {@link Enum#name()} matches
- * (case <U>insensitive</U>) the provided argument - {@code null} if no match
- */
- public static BuiltinSignatures fromString(String s) {
- if (GenericUtils.isEmpty(s)) {
- return null;
- }
-
- for (BuiltinSignatures c : VALUES) {
- if (s.equalsIgnoreCase(c.name())) {
- return c;
- }
- }
-
- return null;
- }
-
- /**
- * @param factory The {@link org.apache.sshd.common.NamedFactory} for the signature - ignored if {@code null}
- * @return The matching {@link org.apache.sshd.common.signature.BuiltinSignatures} whose factory name matches
- * (case <U>insensitive</U>) the digest factory name
- * @see #fromFactoryName(String)
- */
- public static BuiltinSignatures fromFactory(NamedFactory<Signature> 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 BuiltinSignatures} whose factory name matches
- * (case <U>insensitive</U>) the provided name - {@code null} if no match
- */
- public static BuiltinSignatures fromFactoryName(String name) {
- return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
- }
-
- /**
- * @param sigs A comma-separated list of signatures' names - ignored
- * if {@code null}/empty
- * @return A {@link ParseResult} of all the {@link NamedFactory} whose
- * name appears in the string and represent a built-in signature. Any
- * 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
- */
- public static ParseResult parseSignatureList(String sigs) {
- return parseSignatureList(GenericUtils.split(sigs, ','));
- }
-
- public static ParseResult parseSignatureList(String... sigs) {
- return parseSignatureList(GenericUtils.isEmpty((Object[]) sigs) ? Collections.emptyList() : Arrays.asList(sigs));
- }
-
- public static ParseResult parseSignatureList(Collection<String> sigs) {
- if (GenericUtils.isEmpty(sigs)) {
- return ParseResult.EMPTY;
- }
-
- List<SignatureFactory> factories = new ArrayList<>(sigs.size());
- List<String> unknown = Collections.emptyList();
- for (String name : sigs) {
- SignatureFactory s = resolveFactory(name);
- if (s != null) {
- factories.add(s);
- } 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 SignatureFactory resolveFactory(String name) {
- if (GenericUtils.isEmpty(name)) {
- return null;
- }
-
- SignatureFactory s = fromFactoryName(name);
- if (s != null) {
- return s;
- }
-
- synchronized (EXTENSIONS) {
- return EXTENSIONS.get(name);
- }
- }
-
- /**
- * Holds the result of the {@link BuiltinSignatures#parseSignatureList(String)}
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
- public static final class ParseResult extends NamedFactoriesListParseResult<Signature, SignatureFactory> {
- public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
-
- public ParseResult(List<SignatureFactory> parsed, List<String> unsupported) {
- super(parsed, unsupported);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/signature/Signature.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/Signature.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/Signature.java
deleted file mode 100644
index fd88a1d..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/Signature.java
+++ /dev/null
@@ -1,88 +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.signature;
-
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
-import org.apache.sshd.common.util.NumberUtils;
-
-/**
- * Signature interface for SSH used to sign or verify packets
- * Usually wraps a javax.crypto.Signature object
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Signature {
- /**
- * @return The signature algorithm name
- */
- String getAlgorithm();
-
- /**
- * @param key The {@link PublicKey} to be used for verifying signatures
- * @throws Exception If failed to initialize
- */
- void initVerifier(PublicKey key) throws Exception;
-
- /**
- * @param key The {@link PrivateKey} to be used for signing
- * @throws Exception If failed to initialize
- */
- void initSigner(PrivateKey key) throws Exception;
-
- /**
- * Update the computed signature with the given data
- *
- * @param hash The hash data buffer
- * @throws Exception If failed to update
- * @see #update(byte[], int, int)
- */
- default void update(byte[] hash) throws Exception {
- update(hash, 0, NumberUtils.length(hash));
- }
-
- /**
- * Update the computed signature with the given data
- *
- * @param hash The hash data buffer
- * @param off Offset of hash data in buffer
- * @param len Length of hash data
- * @throws Exception If failed to update
- */
- void update(byte[] hash, int off, int len) throws Exception;
-
- /**
- * Verify against the given signature
- *
- * @param sig The signed data
- * @return {@code true} if signature is valid
- * @throws Exception If failed to extract signed data for validation
- */
- boolean verify(byte[] sig) throws Exception;
-
- /**
- * Compute the signature
- *
- * @return The signature value
- * @throws Exception If failed to calculate the signature
- */
- byte[] sign() throws Exception;
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
deleted file mode 100644
index 1f552bd..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
+++ /dev/null
@@ -1,142 +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.signature;
-
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.security.SignatureException;
-import java.util.Map;
-
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.io.der.DERParser;
-import org.apache.sshd.common.util.io.der.DERWriter;
-
-
-/**
- * DSA <code>Signature</code>
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-6.6">RFC4253 section 6.6</A>
- */
-public class SignatureDSA extends AbstractSignature {
- public static final String DEFAULT_ALGORITHM = "SHA1withDSA";
-
- public static final int DSA_SIGNATURE_LENGTH = 40;
- // result must be 40 bytes, but length of r and s may not exceed 20 bytes
- public static final int MAX_SIGNATURE_VALUE_LENGTH = DSA_SIGNATURE_LENGTH / 2;
-
- public SignatureDSA() {
- this(DEFAULT_ALGORITHM);
- }
-
- protected SignatureDSA(String algorithm) {
- super(algorithm);
- }
-
- @Override
- public byte[] sign() throws Exception {
- byte[] sig = super.sign();
-
- try (DERParser parser = new DERParser(sig)) {
- int type = parser.read();
- if (type != 0x30) {
- throw new StreamCorruptedException("Invalid signature format - not a DER SEQUENCE: 0x" + Integer.toHexString(type));
- }
-
- // length of remaining encoding of the 2 integers
- int remainLen = parser.readLength();
- /*
- * There are supposed to be 2 INTEGERs, each encoded with:
- *
- * - one byte representing the fact that it is an INTEGER
- * - one byte of the integer encoding length
- * - at least one byte of integer data (zero length is not an option)
- */
- if (remainLen < (2 * 3)) {
- throw new StreamCorruptedException("Invalid signature format - not enough encoded data length: " + remainLen);
- }
-
- BigInteger r = parser.readBigInteger();
- BigInteger s = parser.readBigInteger();
-
- byte[] result = new byte[DSA_SIGNATURE_LENGTH];
- putBigInteger(r, result, 0);
- putBigInteger(s, result, MAX_SIGNATURE_VALUE_LENGTH);
- return result;
- }
- }
-
- public static void putBigInteger(BigInteger value, byte[] result, int offset) {
- byte[] data = value.toByteArray();
- boolean maxExceeded = data.length > MAX_SIGNATURE_VALUE_LENGTH;
- int dstOffset = maxExceeded ? 0 : (MAX_SIGNATURE_VALUE_LENGTH - data.length);
- System.arraycopy(data, maxExceeded ? 1 : 0,
- result, offset + dstOffset,
- Math.min(MAX_SIGNATURE_VALUE_LENGTH, data.length));
- }
-
- @Override
- public boolean verify(byte[] sig) throws Exception {
- int sigLen = NumberUtils.length(sig);
- byte[] data = sig;
-
- if (sigLen != DSA_SIGNATURE_LENGTH) {
- // probably some encoded data
- Map.Entry<String, byte[]> encoding = extractEncodedSignature(sig);
- if (encoding != null) {
- String keyType = encoding.getKey();
- ValidateUtils.checkTrue(KeyPairProvider.SSH_DSS.equals(keyType), "Mismatched key type: %s", keyType);
- data = encoding.getValue();
- sigLen = NumberUtils.length(data);
- }
- }
-
- if (sigLen != DSA_SIGNATURE_LENGTH) {
- throw new SignatureException("Bad signature length (" + sigLen + " instead of " + DSA_SIGNATURE_LENGTH + ")"
- + " for " + BufferUtils.toHex(':', data));
- }
-
- byte[] rEncoding;
- try (DERWriter w = new DERWriter(MAX_SIGNATURE_VALUE_LENGTH + 4)) { // in case length > 0x7F
- w.writeBigInteger(data, 0, MAX_SIGNATURE_VALUE_LENGTH);
- rEncoding = w.toByteArray();
- }
-
- byte[] sEncoding;
- try (DERWriter w = new DERWriter(MAX_SIGNATURE_VALUE_LENGTH + 4)) { // in case length > 0x7F
- w.writeBigInteger(data, MAX_SIGNATURE_VALUE_LENGTH, MAX_SIGNATURE_VALUE_LENGTH);
- sEncoding = w.toByteArray();
- }
-
- int length = rEncoding.length + sEncoding.length;
- byte[] encoded;
- try (DERWriter w = new DERWriter(1 + length + 4)) { // in case length > 0x7F
- w.write(0x30); // SEQUENCE
- w.writeLength(length);
- w.write(rEncoding);
- w.write(sEncoding);
- encoded = w.toByteArray();
- }
-
- return doVerify(encoded);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
deleted file mode 100644
index 56964d3..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
+++ /dev/null
@@ -1,144 +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.signature;
-
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.util.Map;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-import org.apache.sshd.common.util.io.der.DERParser;
-import org.apache.sshd.common.util.io.der.DERWriter;
-
-/**
- * Signature algorithm for EC keys using ECDSA.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="http://tools.ietf.org/html/rfc3278#section-8.2">RFC3278 section 8.2</A>
- */
-public class SignatureECDSA extends AbstractSignature {
- public static class SignatureECDSA256 extends SignatureECDSA {
- public static final String DEFAULT_ALGORITHM = "SHA256withECDSA";
-
- public SignatureECDSA256() {
- super(DEFAULT_ALGORITHM);
- }
- }
-
- public static class SignatureECDSA384 extends SignatureECDSA {
- public static final String DEFAULT_ALGORITHM = "SHA384withECDSA";
-
- public SignatureECDSA384() {
- super(DEFAULT_ALGORITHM);
- }
- }
-
- public static class SignatureECDSA521 extends SignatureECDSA {
- public static final String DEFAULT_ALGORITHM = "SHA512withECDSA";
-
- public SignatureECDSA521() {
- super(DEFAULT_ALGORITHM);
- }
- }
-
- protected SignatureECDSA(String algo) {
- super(algo);
- }
-
- @Override
- public byte[] sign() throws Exception {
- byte[] sig = super.sign();
-
- try (DERParser parser = new DERParser(sig)) {
- int type = parser.read();
- if (type != 0x30) {
- throw new StreamCorruptedException("Invalid signature format - not a DER SEQUENCE: 0x" + Integer.toHexString(type));
- }
-
- // length of remaining encoding of the 2 integers
- int remainLen = parser.readLength();
- /*
- * There are supposed to be 2 INTEGERs, each encoded with:
- *
- * - one byte representing the fact that it is an INTEGER
- * - one byte of the integer encoding length
- * - at least one byte of integer data (zero length is not an option)
- */
- if (remainLen < (2 * 3)) {
- throw new StreamCorruptedException("Invalid signature format - not enough encoded data length: " + remainLen);
- }
-
- BigInteger r = parser.readBigInteger();
- BigInteger s = parser.readBigInteger();
- // Write the <r,s> to its own types writer.
- Buffer rsBuf = new ByteArrayBuffer();
- rsBuf.putMPInt(r);
- rsBuf.putMPInt(s);
-
- return rsBuf.getCompactData();
- }
- }
-
- @Override
- public boolean verify(byte[] sig) throws Exception {
- byte[] data = sig;
- Map.Entry<String, byte[]> encoding = extractEncodedSignature(data);
- if (encoding != null) {
- String keyType = encoding.getKey();
- ECCurves curve = ECCurves.fromKeyType(keyType);
- ValidateUtils.checkNotNull(curve, "Unknown curve type: %s", keyType);
- data = encoding.getValue();
- }
-
- Buffer rsBuf = new ByteArrayBuffer(data);
- byte[] rArray = rsBuf.getMPIntAsBytes();
- byte[] rEncoding;
- try (DERWriter w = new DERWriter(rArray.length + 4)) { // in case length > 0x7F
- w.writeBigInteger(rArray);
- rEncoding = w.toByteArray();
- }
-
- byte[] sArray = rsBuf.getMPIntAsBytes();
- byte[] sEncoding;
- try (DERWriter w = new DERWriter(sArray.length + 4)) { // in case length > 0x7F
- w.writeBigInteger(sArray);
- sEncoding = w.toByteArray();
- }
-
- int remaining = rsBuf.available();
- if (remaining != 0) {
- throw new StreamCorruptedException("Signature had padding - remaining=" + remaining);
- }
-
- int length = rEncoding.length + sEncoding.length;
- byte[] encoded;
- try (DERWriter w = new DERWriter(1 + length + 4)) { // in case length > 0x7F
- w.write(0x30); // SEQUENCE
- w.writeLength(length);
- w.write(rEncoding);
- w.write(sEncoding);
- encoded = w.toByteArray();
- }
-
- return doVerify(encoded);
- }
-}
[36/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Object.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Object.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Object.java
new file mode 100644
index 0000000..1d32a40
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Object.java
@@ -0,0 +1,338 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.io.der;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ASN1Object implements Serializable, Cloneable {
+ // Constructed Flag
+ public static final byte CONSTRUCTED = 0x20;
+
+ private static final long serialVersionUID = 4687581744706127265L;
+
+ private ASN1Class objClass;
+ private ASN1Type objType;
+ private boolean constructed;
+ private int length;
+ private byte[] value;
+
+ public ASN1Object() {
+ super();
+ }
+
+ /*
+ * <P>The first byte in DER encoding is made of following fields</P>
+ * <pre>
+ *-------------------------------------------------
+ *|Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
+ *-------------------------------------------------
+ *| Class | CF | Type |
+ *-------------------------------------------------
+ * </pre>
+ */
+ public ASN1Object(byte tag, int len, byte... data) {
+ this(ASN1Class.fromDERValue(tag), ASN1Type.fromDERValue(tag), (tag & CONSTRUCTED) == CONSTRUCTED, len, data);
+ }
+
+ public ASN1Object(ASN1Class c, ASN1Type t, boolean ctored, int len, byte... data) {
+ objClass = c;
+ objType = t;
+ constructed = ctored;
+ length = len;
+ value = data;
+ }
+
+ public ASN1Class getObjClass() {
+ return objClass;
+ }
+
+ public void setObjClass(ASN1Class c) {
+ objClass = c;
+ }
+
+ public ASN1Type getObjType() {
+ return objType;
+ }
+
+ public void setObjType(ASN1Type y) {
+ objType = y;
+ }
+
+ public boolean isConstructed() {
+ return constructed;
+ }
+
+ public void setConstructed(boolean c) {
+ constructed = c;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public void setLength(int l) {
+ length = l;
+ }
+
+ public byte[] getValue() {
+ return value;
+ }
+
+ // if length is less than value.length then returns copy of it
+ public byte[] getPureValueBytes() {
+ byte[] bytes = getValue();
+ int available = getLength();
+ int numBytes = NumberUtils.length(bytes);
+ if (numBytes == available) {
+ return bytes;
+ }
+
+ if (available == 0) {
+ return GenericUtils.EMPTY_BYTE_ARRAY;
+ }
+
+ byte[] pure = new byte[available];
+ System.arraycopy(bytes, 0, pure, 0, available);
+ return pure;
+ }
+
+ public void setValue(byte[] v) {
+ value = v;
+ }
+
+ public DERParser createParser() {
+ return new DERParser(getValue(), 0, getLength());
+ }
+
+ public Object asObject() throws IOException {
+ ASN1Type type = getObjType();
+ if (type == null) {
+ throw new IOException("No type set");
+ }
+
+ switch (type) {
+ case INTEGER:
+ return asInteger();
+
+ case NUMERIC_STRING:
+ case PRINTABLE_STRING:
+ case VIDEOTEX_STRING:
+ case IA5_STRING:
+ case GRAPHIC_STRING:
+ case ISO646_STRING:
+ case GENERAL_STRING:
+ case BMP_STRING:
+ case UTF8_STRING:
+ return asString();
+
+ case OBJECT_IDENTIFIER:
+ return asOID();
+
+ case SEQUENCE :
+ return getValue();
+
+ default:
+ throw new IOException("Invalid DER: unsupported type: " + type);
+ }
+ }
+
+ /**
+ * Get the value as {@link BigInteger}
+ * @return BigInteger
+ * @throws IOException if type not an {@link ASN1Type#INTEGER}
+ */
+ public BigInteger asInteger() throws IOException {
+ ASN1Type typeValue = getObjType();
+ if (ASN1Type.INTEGER.equals(typeValue)) {
+ return toInteger();
+ } else {
+ throw new IOException("Invalid DER: object is not integer: " + typeValue);
+ }
+ }
+
+ // does not check if this is an integer
+ public BigInteger toInteger() {
+ return new BigInteger(getPureValueBytes());
+ }
+
+ /**
+ * Get value as string. Most strings are treated as Latin-1.
+ * @return Java string
+ * @throws IOException if
+ */
+ public String asString() throws IOException {
+ ASN1Type type = getObjType();
+ if (type == null) {
+ throw new IOException("No type set");
+ }
+
+ final String encoding;
+ switch (type) {
+ // Not all are Latin-1 but it's the closest thing
+ case NUMERIC_STRING:
+ case PRINTABLE_STRING:
+ case VIDEOTEX_STRING:
+ case IA5_STRING:
+ case GRAPHIC_STRING:
+ case ISO646_STRING:
+ case GENERAL_STRING:
+ encoding = "ISO-8859-1";
+ break;
+
+ case BMP_STRING:
+ encoding = "UTF-16BE";
+ break;
+
+ case UTF8_STRING:
+ encoding = "UTF-8";
+ break;
+
+ case UNIVERSAL_STRING:
+ throw new IOException("Invalid DER: can't handle UCS-4 string");
+
+ default:
+ throw new IOException("Invalid DER: object is not a string: " + type);
+ }
+
+ return new String(getValue(), 0, getLength(), encoding);
+ }
+
+ public List<Integer> asOID() throws IOException {
+ ASN1Type typeValue = getObjType();
+ if (ASN1Type.OBJECT_IDENTIFIER.equals(typeValue)) {
+ return toOID();
+ } else {
+ throw new StreamCorruptedException("Invalid DER: object is not an OID: " + typeValue);
+ }
+ }
+
+ // Does not check that type is OID
+ public List<Integer> toOID() throws IOException {
+ int vLen = getLength();
+ if (vLen <= 0) {
+ throw new EOFException("Not enough data for an OID");
+ }
+
+ List<Integer> oid = new ArrayList<>(vLen + 1);
+ byte[] bytes = getValue();
+ int val1 = bytes[0] & 0xFF;
+ oid.add(Integer.valueOf(val1 / 40));
+ oid.add(Integer.valueOf(val1 % 40));
+
+ for (int curPos = 1; curPos < vLen; curPos++) {
+ int v = bytes[curPos] & 0xFF;
+ if (v <= 0x7F) { // short form
+ oid.add(Integer.valueOf(v));
+ continue;
+ }
+
+ long curVal = v & 0x7F;
+ curPos++;
+
+ for (int subLen = 1;; subLen++, curPos++) {
+ if (curPos >= vLen) {
+ throw new EOFException("Incomplete OID value");
+ }
+
+ if (subLen > 5) { // 32 bit values can span at most 5 octets
+ throw new StreamCorruptedException("OID component encoding beyond 5 bytes");
+ }
+
+ v = bytes[curPos] & 0xFF;
+ curVal = ((curVal << 7) & 0xFFFFFFFF80L) | (v & 0x7FL);
+ if (curVal > Integer.MAX_VALUE) {
+ throw new StreamCorruptedException("OID value exceeds 32 bits: " + curVal);
+ }
+
+ if (v <= 0x7F) { // found last octet ?
+ break;
+ }
+ }
+
+ oid.add(Integer.valueOf((int) (curVal & 0x7FFFFFFFL)));
+ }
+
+ return oid;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getObjClass(), getObjType())
+ + Boolean.hashCode(isConstructed())
+ + getLength()
+ + NumberUtils.hashCode(getValue(), 0, getLength());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ ASN1Object other = (ASN1Object) obj;
+ return Objects.equals(this.getObjClass(), other.getObjClass())
+ && Objects.equals(this.getObjType(), other.getObjType())
+ && (this.isConstructed() == other.isConstructed())
+ && (this.getLength() == other.getLength())
+ && (NumberUtils.diffOffset(this.getValue(), 0, other.getValue(), 0, this.getLength()) < 0);
+ }
+
+ @Override
+ public ASN1Object clone() {
+ try {
+ ASN1Object cpy = getClass().cast(super.clone());
+ byte[] data = cpy.getValue();
+ if (data != null) {
+ cpy.setValue(data.clone());
+ }
+ return cpy;
+ } catch (CloneNotSupportedException e) {
+ throw new IllegalStateException("Unexpected clone failure: " + e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toString(getObjClass())
+ + "/" + getObjType()
+ + "/" + isConstructed()
+ + "[" + getLength() + "]"
+ + ": " + BufferUtils.toHex(getValue(), 0, getLength(), ':');
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Type.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Type.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Type.java
new file mode 100644
index 0000000..3dd13ca
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Type.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.io.der;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum ASN1Type {
+ ANY((byte) 0x00),
+ BOOLEAN((byte) 0x01),
+ INTEGER((byte) 0x02),
+ BIT_STRING((byte) 0x03),
+ OCTET_STRING((byte) 0x04),
+ NULL((byte) 0x05),
+ OBJECT_IDENTIFIER((byte) 0x06),
+ REAL((byte) 0x09),
+ ENUMERATED((byte) 0x0a),
+ RELATIVE_OID((byte) 0x0d),
+ SEQUENCE((byte) 0x10),
+ SET((byte) 0x11),
+ NUMERIC_STRING((byte) 0x12),
+ PRINTABLE_STRING((byte) 0x13),
+ T61_STRING((byte) 0x14),
+ VIDEOTEX_STRING((byte) 0x15),
+ IA5_STRING((byte) 0x16),
+ GRAPHIC_STRING((byte) 0x19),
+ ISO646_STRING((byte) 0x1A),
+ GENERAL_STRING((byte) 0x1B),
+ UTF8_STRING((byte) 0x0C),
+ UNIVERSAL_STRING((byte) 0x1C),
+ BMP_STRING((byte) 0x1E),
+ UTC_TIME((byte) 0x17),
+ GENERALIZED_TIME((byte) 0x18);
+
+ public static final Set<ASN1Type> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(ASN1Type.class));
+
+ private final byte typeValue;
+
+ ASN1Type(byte typeVal) {
+ typeValue = typeVal;
+ }
+
+ public byte getTypeValue() {
+ return typeValue;
+ }
+
+ public static ASN1Type fromName(String s) {
+ if (GenericUtils.isEmpty(s)) {
+ return null;
+ }
+
+ for (ASN1Type t : VALUES) {
+ if (s.equalsIgnoreCase(t.name())) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * <P>The first byte in DER encoding is made of following fields</P>
+ * <pre>
+ *-------------------------------------------------
+ *|Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
+ *-------------------------------------------------
+ *| Class | CF | Type |
+ *-------------------------------------------------
+ * </pre>
+ * @param value The original DER encoded byte
+ * @return The {@link ASN1Type} value - {@code null} if no match found
+ * @see #fromTypeValue(int)
+ */
+ public static ASN1Type fromDERValue(int value) {
+ return fromTypeValue(value & 0x1F);
+ }
+
+ /**
+ * @param value The "pure" type value - with no extra bits set
+ * @return The {@link ASN1Type} value - {@code null} if no match found
+ */
+ public static ASN1Type fromTypeValue(int value) {
+ if ((value < 0) || (value > 0x1F)) { // only 5 bits are used
+ return null;
+ }
+
+ for (ASN1Type t : VALUES) {
+ if (t.getTypeValue() == value) {
+ return t;
+ }
+ }
+
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
new file mode 100644
index 0000000..5f37bd7
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io.der;
+
+import java.io.ByteArrayInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.util.Arrays;
+
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+
+/**
+ * A bare minimum DER parser - just enough to be able to decode
+ * signatures and private keys
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DERParser extends FilterInputStream {
+ /**
+ * Maximum size of data allowed by {@link #readLength()} - it is a bit
+ * arbitrary since one can encode 32-bit length data, but it is good
+ * enough for the keys
+ */
+ public static final int MAX_DER_VALUE_LENGTH = 2 * Short.MAX_VALUE;
+
+ private final byte[] lenBytes = new byte[Integer.BYTES];
+
+ public DERParser(byte... bytes) {
+ this(bytes, 0, NumberUtils.length(bytes));
+ }
+
+ public DERParser(byte[] bytes, int offset, int len) {
+ this(new ByteArrayInputStream(bytes, offset, len));
+ }
+
+ public DERParser(InputStream s) {
+ super(s);
+ }
+
+ /**
+ * Decode the length of the field. Can only support length
+ * encoding up to 4 octets. In BER/DER encoding, length can
+ * be encoded in 2 forms:
+ * <ul>
+ * <li><p>
+ * Short form - One octet. Bit 8 has value "0" and bits 7-1
+ * give the length.
+ * </p></li>
+ *
+ * <li><p>
+ * Long form - Two to 127 octets (only 4 is supported here).
+ * Bit 8 of first octet has value "1" and bits 7-1 give the
+ * number of additional length octets. Second and following
+ * octets give the length, base 256, most significant digit
+ * first.
+ * </p></li>
+ * </ul>
+ *
+ * @return The length as integer
+ * @throws IOException If invalid format found
+ */
+ public int readLength() throws IOException {
+ int i = read();
+ if (i == -1) {
+ throw new StreamCorruptedException("Invalid DER: length missing");
+ }
+
+ // A single byte short length
+ if ((i & ~0x7F) == 0) {
+ return i;
+ }
+
+ int num = i & 0x7F;
+ // TODO We can't handle length longer than 4 bytes
+ if ((i >= 0xFF) || (num > lenBytes.length)) {
+ throw new StreamCorruptedException("Invalid DER: length field too big: " + i);
+ }
+
+ // place the read bytes last so that the 1st ones are zeroes as big endian
+ Arrays.fill(lenBytes, (byte) 0);
+ int n = read(lenBytes, 4 - num, num);
+ if (n < num) {
+ throw new StreamCorruptedException("Invalid DER: length data too short: expected=" + num + ", actual=" + n);
+ }
+
+ long len = BufferUtils.getUInt(lenBytes);
+ if (len < 0x7FL) { // according to standard: "the shortest possible length encoding must be used"
+ throw new StreamCorruptedException("Invalid DER: length not in shortest form: " + len);
+ }
+
+ if (len > MAX_DER_VALUE_LENGTH) {
+ throw new StreamCorruptedException("Invalid DER: data length too big: " + len + " (max=" + MAX_DER_VALUE_LENGTH + ")");
+ }
+
+ // we know the cast is safe since it is less than MAX_DER_VALUE_LENGTH which is ~64K
+ return (int) len;
+ }
+
+ public ASN1Object readObject() throws IOException {
+ int tag = read();
+ if (tag == -1) {
+ return null;
+ }
+
+ int length = readLength();
+ byte[] value = new byte[length];
+ int n = read(value);
+ if (n < length) {
+ throw new StreamCorruptedException("Invalid DER: stream too short, missing value: read " + n + " out of required " + length);
+ }
+
+ return new ASN1Object((byte) tag, length, value);
+ }
+
+ public BigInteger readBigInteger() throws IOException {
+ int type = read();
+ if (type != 0x02) {
+ throw new StreamCorruptedException("Invalid DER: data type is not an INTEGER: 0x" + Integer.toHexString(type));
+ }
+
+ int len = readLength();
+ byte[] value = new byte[len];
+ int n = read(value);
+ if (n < len) {
+ throw new StreamCorruptedException("Invalid DER: stream too short, missing value: read " + n + " out of required " + len);
+ }
+
+ return new BigInteger(value);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java
new file mode 100644
index 0000000..bc603ee
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io.der;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+
+/**
+ * A bare-minimum DER encoder - just enough so we can encoder signatures
+ * and keys data
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DERWriter extends FilterOutputStream {
+ private final byte[] lenBytes = new byte[Integer.BYTES];
+
+ public DERWriter() {
+ this(ByteArrayBuffer.DEFAULT_SIZE);
+ }
+
+ public DERWriter(int initialSize) {
+ this(new ByteArrayOutputStream(initialSize));
+ }
+
+ public DERWriter(OutputStream stream) {
+ super(Objects.requireNonNull(stream, "No output stream"));
+ }
+
+ public DERWriter startSequence() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ AtomicBoolean dataWritten = new AtomicBoolean(false);
+ @SuppressWarnings("resource")
+ DERWriter encloser = this;
+ return new DERWriter(baos) {
+ @Override
+ public void close() throws IOException {
+ baos.close();
+
+ if (!dataWritten.getAndSet(true)) { // detect repeated calls and write this only once
+ encloser.writeObject(new ASN1Object(ASN1Class.UNIVERSAL, ASN1Type.SEQUENCE, false, baos.size(), baos.toByteArray()));
+ }
+ }
+ };
+ }
+
+ public void writeBigInteger(BigInteger value) throws IOException {
+ writeBigInteger(Objects.requireNonNull(value, "No value").toByteArray());
+ }
+
+ /**
+ * The integer is always considered to be positive, so if the first byte is < 0,
+ * we pad with a zero to make it positive
+ *
+ * @param bytes {@link BigInteger} bytes
+ * @throws IOException If failed to write the bytes
+ */
+ public void writeBigInteger(byte... bytes) throws IOException {
+ writeBigInteger(bytes, 0, NumberUtils.length(bytes));
+ }
+
+ /**
+ * The integer is always considered to be positive, so if the first byte is < 0,
+ * we pad with a zero to make it positive
+ *
+ * @param bytes {@link BigInteger} bytes
+ * @param off Offset in bytes data
+ * @param len Number of bytes to write
+ * @throws IOException If failed to write the bytes
+ */
+ public void writeBigInteger(byte[] bytes, int off, int len) throws IOException {
+ // Strip leading zeroes
+ while (len > 1 && bytes[off] == 0 && isPositive(bytes[off + 1])) {
+ off++;
+ len--;
+ }
+ // indicate it is an INTEGER
+ write(0x02);
+ // Pad with a zero if needed
+ if (isPositive(bytes[off])) {
+ writeLength(len);
+ } else {
+ writeLength(len + 1);
+ write(0);
+ }
+ // Write data
+ write(bytes, off, len);
+ }
+
+ private boolean isPositive(byte b) {
+ return (b & 0x80) == 0;
+ }
+
+ public void writeObject(ASN1Object obj) throws IOException {
+ Objects.requireNonNull(obj, "No ASN.1 object");
+
+ ASN1Type type = obj.getObjType();
+ byte typeValue = type.getTypeValue();
+ ASN1Class clazz = obj.getObjClass();
+ byte classValue = clazz.getClassValue();
+ byte tagValue = (byte) (((classValue << 6) & 0xC0) | (typeValue & 0x1F));
+ writeObject(tagValue, obj.getLength(), obj.getValue());
+ }
+
+ public void writeObject(byte tag, int len, byte... data) throws IOException {
+ write(tag & 0xFF);
+ writeLength(len);
+ write(data, 0, len);
+ }
+
+ public void writeLength(int len) throws IOException {
+ ValidateUtils.checkTrue(len >= 0, "Invalid length: %d", len);
+
+ // short form - MSBit is zero
+ if (len <= 127) {
+ write(len);
+ return;
+ }
+
+ BufferUtils.putUInt(len, lenBytes);
+
+ int nonZeroPos = 0;
+ for (; nonZeroPos < lenBytes.length; nonZeroPos++) {
+ if (lenBytes[nonZeroPos] != 0) {
+ break;
+ }
+ }
+
+ if (nonZeroPos >= lenBytes.length) {
+ throw new StreamCorruptedException("All zeroes length representation for len=" + len);
+ }
+
+ int bytesLen = lenBytes.length - nonZeroPos;
+ write(0x80 | bytesLen); // indicate number of octets
+ write(lenBytes, nonZeroPos, bytesLen);
+ }
+
+ public byte[] toByteArray() throws IOException {
+ if (this.out instanceof ByteArrayOutputStream) {
+ return ((ByteArrayOutputStream) this.out).toByteArray();
+ } else {
+ throw new IOException("The underlying stream is not a byte[] stream");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java
new file mode 100644
index 0000000..a55ac2e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/functors/IOFunction.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io.functors;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Invokes some I/O function on the input returning some output
+ * and potentially throwing an {@link IOException} in the process
+ *
+ * @param <T> Type of input
+ * @param <R> Type of output
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface IOFunction<T, R> {
+ R apply(T t) throws IOException;
+
+ /**
+ * Returns a composed function that first applies the {@code before}
+ * function to its input, and then applies this function to the result.
+ * If evaluation of either function throws an exception, it is relayed to
+ * the caller of the composed function.
+ *
+ * @param <V> the type of input to the {@code before} function, and to the
+ * composed function
+ * @param before the function to apply before this function is applied
+ * @return a composed function that first applies the {@code before}
+ * function and then applies this function
+ * @throws NullPointerException if before is null
+ *
+ * @see #andThen(IOFunction)
+ */
+ default <V> IOFunction<V, R> compose(IOFunction<? super V, ? extends T> before) {
+ Objects.requireNonNull(before, "No composing function provided");
+ return (V v) -> apply(before.apply(v));
+ }
+
+ /**
+ * Returns a composed function that first applies this function to
+ * its input, and then applies the {@code after} function to the result.
+ * If evaluation of either function throws an exception, it is relayed to
+ * the caller of the composed function.
+ *
+ * @param <V> the type of output of the {@code after} function, and of the
+ * composed function
+ * @param after the function to apply after this function is applied
+ * @return a composed function that first applies this function and then
+ * applies the {@code after} function
+ * @throws NullPointerException if after is null
+ *
+ * @see #compose(IOFunction)
+ */
+ default <V> IOFunction<T, V> andThen(IOFunction<? super R, ? extends V> after) {
+ Objects.requireNonNull(after, "No composing function provided");
+ return (T t) -> after.apply(apply(t));
+ }
+
+ /**
+ * Returns a function that always returns its input argument.
+ *
+ * @param <T> the type of the input and output objects to the function
+ * @return a function that always returns its input argument
+ */
+ static <T> IOFunction<T, T> identity() {
+ return t -> t;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.java
new file mode 100644
index 0000000..27fef2f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/AbstractLoggingBean.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.util.logging;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Serves as a common base class for the vast majority of classes that require
+ * some kind of logging. Facilitates quick and easy replacement of the actual used
+ * logger from one framework to another
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractLoggingBean {
+ protected final Logger log;
+ private final AtomicReference<SimplifiedLog> simplifiedLog = new AtomicReference<>();
+
+ /**
+ * Default constructor - creates a logger using the full class name
+ */
+ protected AbstractLoggingBean() {
+ this("");
+ }
+
+ /**
+ * Create a logger for instances of the same class for which we might
+ * want to have a "discriminator" for them
+ *
+ * @param discriminator The discriminator value - ignored if {@code null}
+ * or empty
+ */
+ protected AbstractLoggingBean(String discriminator) {
+ String name = getClass().getName();
+ if (GenericUtils.length(discriminator) > 0) {
+ name += "[" + discriminator + "]";
+ }
+ log = LoggerFactory.getLogger(name);
+ }
+
+ protected SimplifiedLog getSimplifiedLogger() {
+ SimplifiedLog logger;
+ synchronized (simplifiedLog) {
+ logger = simplifiedLog.get();
+ if (logger == null) {
+ logger = LoggingUtils.wrap(log);
+ }
+ }
+
+ return logger;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
new file mode 100644
index 0000000..674ed1a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
@@ -0,0 +1,549 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.logging;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.Objects;
+import java.util.TreeMap;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.logging.Level;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ReflectionUtils;
+import org.slf4j.Logger;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class LoggingUtils {
+
+ private LoggingUtils() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ /**
+ * Scans using reflection API for all fields that are {@code public static final}
+ * that start with the given common prefix (case <U>sensitive</U>) and are of type
+ * {@link Number}.
+ *
+ * @param clazz The {@link Class} to query
+ * @param commonPrefix The expected common prefix
+ * @return A {@link NavigableMap} of all the matching fields, where key=the field's {@link Integer}
+ * value and mapping=the field's name
+ * @see #generateMnemonicMap(Class, Predicate)
+ */
+ public static NavigableMap<Integer, String> generateMnemonicMap(Class<?> clazz, final String commonPrefix) {
+ return generateMnemonicMap(clazz, f -> {
+ String name = f.getName();
+ return name.startsWith(commonPrefix);
+ });
+ }
+
+ /**
+ * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
+ * that are also accepted by the predicate. Any field that is not such or fail to retrieve
+ * its value, or has a duplicate value is <U>silently</U> skipped.
+ *
+ * @param clazz The {@link Class} to query
+ * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
+ * (besides being a {@link Number} and {@code public static final}).
+ * @return A {@link NavigableMap} of all the matching fields, where key=the field's {@link Integer}
+ * value and mapping=the field's name
+ * @see #getMnemonicFields(Class, Predicate)
+ */
+ public static NavigableMap<Integer, String> generateMnemonicMap(Class<?> clazz, Predicate<? super Field> acceptor) {
+ Collection<Field> fields = getMnemonicFields(clazz, acceptor);
+ if (GenericUtils.isEmpty(fields)) {
+ return Collections.emptyNavigableMap();
+ }
+
+ NavigableMap<Integer, String> result = new TreeMap<>(Comparator.naturalOrder());
+ for (Field f : fields) {
+ String name = f.getName();
+ try {
+ Number value = (Number) f.get(null);
+ String prev = result.put(NumberUtils.toInteger(value), name);
+ if (prev != null) {
+ //noinspection UnnecessaryContinue
+ continue; // debug breakpoint
+ }
+ } catch (Exception e) {
+ //noinspection UnnecessaryContinue
+ continue; // debug breakpoint
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
+ * that have a common prefix and whose value is used by several of the other
+ * matching fields
+ *
+ * @param clazz The {@link Class} to query
+ * @param commonPrefix The expected common prefix
+ * @return A {@link Map} of all the mnemonic fields names whose value is the same as other
+ * fields in this map. The key is the field's name and value is its associated opcode.
+ * @see #getAmbiguousMenmonics(Class, Predicate)
+ */
+ public static Map<String, Integer> getAmbiguousMenmonics(Class<?> clazz, String commonPrefix) {
+ return getAmbiguousMenmonics(clazz, f -> {
+ String name = f.getName();
+ return name.startsWith(commonPrefix);
+ });
+ }
+
+ /**
+ * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
+ * that are also accepted by the predicate and whose value is used by several of the other
+ * matching fields
+ *
+ * @param clazz The {@link Class} to query
+ * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
+ * (besides being a {@link Number} and {@code public static final}).
+ * @return A {@link Map} of all the mnemonic fields names whose value is the same as other
+ * fields in this map. The key is the field's name and value is its associated opcode.
+ * @see #getMnemonicFields(Class, Predicate)
+ */
+ public static Map<String, Integer> getAmbiguousMenmonics(Class<?> clazz, Predicate<? super Field> acceptor) {
+ Collection<Field> fields = getMnemonicFields(clazz, acceptor);
+ if (GenericUtils.isEmpty(fields)) {
+ return Collections.emptyMap();
+ }
+
+ Map<String, Integer> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ Map<Integer, List<String>> opcodesMap = new TreeMap<>(Comparator.naturalOrder());
+ for (Field f : fields) {
+ String name = f.getName();
+ try {
+ Number value = (Number) f.get(null);
+ Integer key = NumberUtils.toInteger(value);
+ List<String> nameList = opcodesMap.get(key);
+ if (nameList == null) {
+ nameList = new ArrayList<>();
+ opcodesMap.put(key, nameList);
+ }
+ nameList.add(name);
+
+ int numOpcodes = nameList.size();
+ if (numOpcodes > 1) {
+ result.put(name, key);
+ if (numOpcodes == 2) { // add the 1st name as well
+ result.put(nameList.get(0), key);
+ }
+ }
+ } catch (Exception e) {
+ continue; // debug breakpoint
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
+ * that are also accepted by the predicate.
+ *
+ * @param clazz The {@link Class} to query
+ * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
+ * (besides being a {@link Number} and {@code public static final}).
+ * @return A {@link Collection} of all the fields that have satisfied all conditions
+ */
+ public static Collection<Field> getMnemonicFields(Class<?> clazz, Predicate<? super Field> acceptor) {
+ return ReflectionUtils.getMatchingFields(clazz, f -> {
+ int mods = f.getModifiers();
+ if ((!Modifier.isPublic(mods)) || (!Modifier.isStatic(mods)) || (!Modifier.isFinal(mods))) {
+ return false;
+ }
+
+ Class<?> type = f.getType();
+ if (!NumberUtils.isNumericClass(type)) {
+ return false;
+ }
+
+ return acceptor.test(f);
+ });
+ }
+
+ /**
+ * Verifies if the given level is above the required threshold for logging.
+ *
+ * @param level The {@link Level} to evaluate
+ * @param threshold The threshold {@link Level}
+ * @return {@code true} if the evaluated level is above the required
+ * threshold.
+ * <P>
+ * <B>Note(s):</B>
+ * </P>
+ * <UL>
+ * <LI><P>
+ * If either argument is {@code null} then result is {@code false}.
+ * </P></LI>
+ *
+ * <LI><P>
+ * If the evaluated level is {@link Level#OFF} then result is {@code false}
+ * regardless of the threshold.
+ * </P></LI>
+ *
+ * <LI><P>
+ * If the threshold is {@link Level#ALL} and the evaluated level is
+ * <U>not</U> {@link Level#OFF} the result is {@code true}.
+ * </P></LI>
+ *
+ * <LI><P>
+ * Otherwise, the evaluated level {@link Level#intValue()} must be
+ * greater or equal to the threshold.
+ * </P></LI>
+ * </UL>
+ */
+ public static boolean isLoggable(Level level, Level threshold) {
+ if ((level == null) || (threshold == null)) {
+ return false;
+ } else if (Level.OFF.equals(level) || Level.OFF.equals(threshold)) {
+ return false;
+ } else if (Level.ALL.equals(threshold)) {
+ return true;
+ } else {
+ return level.intValue() >= threshold.intValue();
+ }
+ }
+
+ public static SimplifiedLog wrap(final Logger logger) {
+ if (logger == null) {
+ return SimplifiedLog.EMPTY;
+ } else {
+ return new SimplifiedLog() {
+ @Override
+ public void log(Level level, Object message, Throwable t) {
+ if (isEnabled(level)) {
+ logMessage(logger, level, message, t);
+ }
+
+ }
+
+ @Override
+ public boolean isEnabled(Level level) {
+ return isLoggable(logger, level);
+ }
+ };
+ }
+ }
+
+ // NOTE: assume that level enabled has been checked !!!
+ public static void logMessage(Logger logger, Level level, Object message, Throwable t) {
+ if ((logger == null) || (level == null) || Level.OFF.equals(level)) {
+ return;
+ } else if (Level.SEVERE.equals(level)) {
+ logger.error(Objects.toString(message), t);
+ } else if (Level.WARNING.equals(level)) {
+ logger.warn(Objects.toString(message), t);
+ } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) {
+ logger.info(Objects.toString(message), t);
+ } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) {
+ logger.debug(Objects.toString(message), t);
+ } else {
+ logger.trace(Objects.toString(message), t);
+ }
+ }
+
+ /**
+ * @param logger The {@link Logger} instance - ignored if {@code null}
+ * @param level The validate log {@link Level} - ignored if {@code null}
+ * @return <P>{@code true} if the level is enabled for the logger. The
+ * mapping of the level to the logger is as follows:</P>
+ * <UL>
+ * <LI>{@link Level#OFF} always returns {@code false}</LI>
+ * <LI>{@link Level#SEVERE} returns {@link Logger#isErrorEnabled()}</LI>
+ * <LI>{@link Level#WARNING} returns {@link Logger#isWarnEnabled()}</LI>
+ * <LI>{@link Level#INFO} and {@link Level#ALL} returns {@link Logger#isInfoEnabled()}</LI>
+ * <LI>{@link Level#CONFIG} and {@link Level#FINE} returns {@link Logger#isDebugEnabled()}</LI>
+ * <LI>All other levels return {@link Logger#isTraceEnabled()}</LI>
+ * </UL>
+ */
+ public static boolean isLoggable(Logger logger, Level level) {
+ if ((logger == null) || (level == null) || Level.OFF.equals(level)) {
+ return false;
+ } else if (Level.SEVERE.equals(level)) {
+ return logger.isErrorEnabled();
+ } else if (Level.WARNING.equals(level)) {
+ return logger.isWarnEnabled();
+ } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) {
+ return logger.isInfoEnabled();
+ } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) {
+ return logger.isDebugEnabled();
+ } else {
+ return logger.isTraceEnabled();
+ }
+ }
+
+ /**
+ * @param <T> Generic message type consumer
+ * @param logger The {@link Logger} instance to use
+ * @param level The log {@link Level} mapped as follows:</BR>
+ *
+ * <UL>
+ * <LI>{@link Level#OFF} - {@link #nologClosure(Logger)}</LI>
+ * <LI>{@link Level#SEVERE} - {@link #errorClosure(Logger)}</LI>
+ * <LI>{@link Level#WARNING} - {@link #warnClosure(Logger)}</LI>
+ * <LI>{@link Level#INFO}/{@link Level#ALL} - {@link #infoClosure(Logger)}</LI>
+ * <LI>{@link Level#CONFIG}/{@link Level#FINE} - {@link #debugClosure(Logger)}</LI>
+ * <LI>All others - {@link #traceClosure(Logger)}</LI>
+ * </UL>
+ * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+ * {@link String#valueOf(Object)} value of its argument if the specific level is enabled
+ */
+ public static <T> Consumer<T> loggingClosure(Logger logger, Level level) {
+ return loggingClosure(logger, level, null);
+ }
+
+ public static <T> Consumer<T> loggingClosure(Logger logger, Level level, Throwable t) {
+ Objects.requireNonNull(level, "No level provided");
+
+ if (Level.OFF.equals(level)) {
+ return nologClosure(logger);
+ } else if (Level.SEVERE.equals(level)) {
+ return errorClosure(logger, t);
+ } else if (Level.WARNING.equals(level)) {
+ return warnClosure(logger, t);
+ } else if (Level.INFO.equals(level) || Level.ALL.equals(level)) {
+ return infoClosure(logger, t);
+ } else if (Level.CONFIG.equals(level) || Level.FINE.equals(level)) {
+ return debugClosure(logger, t);
+ } else {
+ return traceClosure(logger, t);
+ }
+ }
+
+ /**
+ * @param <T> Generic message type consumer
+ * @param logger The {@link Logger} instance to use
+ * @return A consumer whose {@link Consumer#accept(Object)} method logs nothing when invoked
+ */
+ public static <T> Consumer<T> nologClosure(Logger logger) {
+ Objects.requireNonNull(logger, "No logger provided");
+ return t -> { /* do nothing */ };
+ }
+
+ /**
+ * @param <T> Generic message type consumer
+ * @param logger The {@link Logger} instance to use
+ * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+ * {@link String#valueOf(Object)} value of its argument if {@link Logger#isErrorEnabled()}
+ */
+ public static <T> Consumer<T> errorClosure(Logger logger) {
+ return errorClosure(logger, null);
+ }
+
+ /**
+ * @param <T> Generic message type consumer
+ * @param logger The {@link Logger} instance to use
+ * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
+ * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+ * {@link String#valueOf(Object)} value of its argument if {@link Logger#isErrorEnabled()}
+ */
+ public static <T> Consumer<T> errorClosure(Logger logger, Throwable thrown) {
+ Objects.requireNonNull(logger, "No logger provided");
+ return new Consumer<T>() {
+ @Override
+ public void accept(T input) {
+ if (logger.isErrorEnabled()) {
+ String msg = String.valueOf(input);
+ if (thrown == null) {
+ logger.error(msg);
+ } else {
+ logger.error(msg, thrown);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ERROR";
+ }
+ };
+ }
+
+ /**
+ * @param <T> Generic message type consumer
+ * @param logger The {@link Logger} instance to use
+ * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+ * {@link String#valueOf(Object)} value of its argument if {@link Logger#isWarnEnabled()}
+ */
+ public static <T> Consumer<T> warnClosure(Logger logger) {
+ return warnClosure(logger, null);
+ }
+
+ /**
+ * @param <T> Generic message type consumer
+ * @param logger The {@link Logger} instance to use
+ * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
+ * @return A consumer whose {@link Consumer#accept(Object)} method logs the {@link String#valueOf(Object)}
+ * value of its argument if {@link Logger#isWarnEnabled()}
+ */
+ public static <T> Consumer<T> warnClosure(Logger logger, Throwable thrown) {
+ Objects.requireNonNull(logger, "No logger provided");
+ return new Consumer<T>() {
+ @Override
+ public void accept(T input) {
+ if (logger.isWarnEnabled()) {
+ String msg = String.valueOf(input);
+ if (thrown == null) {
+ logger.warn(msg);
+ } else {
+ logger.warn(msg, thrown);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "WARN";
+ }
+ };
+ }
+
+ /**
+ * @param <T> Generic message type consumer
+ * @param logger The {@link Logger} instance to use
+ * @return A consumer whose {@link Consumer#accept(Object)} method logs the {@link String#valueOf(Object)}
+ * value of its argument if {@link Logger#isInfoEnabled()}
+ */
+ public static <T> Consumer<T> infoClosure(Logger logger) {
+ return infoClosure(logger, null);
+ }
+
+ /**
+ * @param <T> Generic message type consumer
+ * @param logger The {@link Logger} instance to use
+ * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
+ * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+ * {@link String#valueOf(Object)} value of its argument if {@link Logger#isInfoEnabled()}
+ */
+ public static <T> Consumer<T> infoClosure(Logger logger, Throwable thrown) {
+ Objects.requireNonNull(logger, "No logger provided");
+ return new Consumer<T>() {
+ @Override
+ public void accept(T input) {
+ if (logger.isInfoEnabled()) {
+ String msg = String.valueOf(input);
+ if (thrown == null) {
+ logger.info(msg);
+ } else {
+ logger.info(msg, thrown);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "INFO";
+ }
+ };
+ }
+
+ /**
+ * @param <T> Generic message type consumer
+ * @param logger The {@link Logger} instance to use
+ * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+ * {@link String#valueOf(Object)} value of its argument if {@link Logger#isDebugEnabled()}
+ */
+ public static <T> Consumer<T> debugClosure(Logger logger) {
+ return debugClosure(logger, null);
+ }
+
+ /**
+ * @param <T> Generic message type consumer
+ * @param logger The {@link Logger} instance to use
+ * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
+ * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+ * {@link String#valueOf(Object)} value of its argument if {@link Logger#isDebugEnabled()}
+ */
+ public static <T> Consumer<T> debugClosure(Logger logger, Throwable thrown) {
+ Objects.requireNonNull(logger, "No logger provided");
+ return new Consumer<T>() {
+ @Override
+ public void accept(T input) {
+ if (logger.isDebugEnabled()) {
+ String msg = String.valueOf(input);
+ if (thrown == null) {
+ logger.debug(msg);
+ } else {
+ logger.debug(msg, thrown);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "DEBUG";
+ }
+ };
+ }
+
+ /**
+ * @param <T> Generic message type consumer
+ * @param logger The {@link Logger} instance to use
+ * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+ * {@link String#valueOf(Object)} value of its argument if {@link Logger#isTraceEnabled()}
+ */
+ public static <T> Consumer<T> traceClosure(Logger logger) {
+ return traceClosure(logger, null);
+ }
+
+ /**
+ * @param <T> Generic message type consumer
+ * @param logger The {@link Logger} instance to use
+ * @param thrown A {@link Throwable} to attach to the message - ignored if {@code null}
+ * @return A consumer whose {@link Consumer#accept(Object)} method logs the
+ * {@link String#valueOf(Object)} value of its argument if {@link Logger#isTraceEnabled()}
+ */
+ public static <T> Consumer<T> traceClosure(Logger logger, Throwable thrown) {
+ Objects.requireNonNull(logger, "No logger provided");
+ return new Consumer<T>() {
+ @Override
+ public void accept(T input) {
+ if (logger.isTraceEnabled()) {
+ String msg = String.valueOf(input);
+ if (thrown == null) {
+ logger.trace(msg);
+ } else {
+ logger.trace(msg, thrown);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "TRACE";
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java
new file mode 100644
index 0000000..58d58ee
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/logging/SimplifiedLog.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.logging;
+
+import java.util.logging.Level;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SimplifiedLog {
+
+ /**
+ * An "empty" {@link SimplifiedLog} that does nothing
+ */
+ SimplifiedLog EMPTY = new SimplifiedLog() {
+ @Override
+ public boolean isEnabled(Level level) {
+ return false;
+ }
+
+ @Override
+ public void log(Level level, Object message, Throwable t) {
+ // ignored
+ }
+
+ @Override
+ public String toString() {
+ return "EMPTY";
+ }
+ };
+
+ boolean isEnabled(Level level);
+
+ default void log(Level level, Object message) {
+ log(level, message, null);
+ }
+
+ void log(Level level, Object message, Throwable t);
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/net/NetworkConnector.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/net/NetworkConnector.java b/sshd-common/src/main/java/org/apache/sshd/common/util/net/NetworkConnector.java
new file mode 100644
index 0000000..df7683a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/net/NetworkConnector.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.net;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NetworkConnector extends AbstractLoggingBean {
+ public static final String DEFAULT_HOST = SshdSocketAddress.LOCALHOST_IPV4;
+ public static final long DEFAULT_CONNECT_TIMEOUT = TimeUnit.SECONDS.toMillis(5L);
+ public static final long DEFAULT_READ_TIMEOUT = TimeUnit.SECONDS.toMillis(15L);
+
+ private String protocol;
+ private String host = DEFAULT_HOST;
+ private int port;
+ private long connectTimeout = DEFAULT_CONNECT_TIMEOUT;
+ private long readTimeout = DEFAULT_READ_TIMEOUT;
+
+ public NetworkConnector() {
+ super();
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public long getConnectTimeout() {
+ return connectTimeout;
+ }
+
+ public void setConnectTimeout(long connectTimeout) {
+ this.connectTimeout = connectTimeout;
+ }
+
+ public long getReadTimeout() {
+ return readTimeout;
+ }
+
+ public void setReadTimeout(long readTimeout) {
+ this.readTimeout = readTimeout;
+ }
+
+ @Override
+ public String toString() {
+ return getProtocol() + "://" + getHost() + ":" + getPort()
+ + ";connect=" + getConnectTimeout()
+ + ";read=" + getReadTimeout();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java b/sshd-common/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java
new file mode 100644
index 0000000..b465d14
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/net/SshdSocketAddress.java
@@ -0,0 +1,639 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.net;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * <P>A simple socket address holding the host name and port number. The reason
+ * it does not extend {@link InetSocketAddress} is twofold:</P>
+ * <OL>
+ * <LI><P>
+ * The {@link InetSocketAddress} performs a DNS resolution on the
+ * provided host name - which we don't want do use until we want to
+ * create a connection using this address (thus the {@link #toInetSocketAddress()}
+ * call which executes this query
+ * </P></LI>
+ *
+ * <LI><P>
+ * If empty host name is provided we replace it with the <I>any</I>
+ * address of 0.0.0.0
+ * </P></LI>
+ * </OL>
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SshdSocketAddress extends SocketAddress {
+ public static final String LOCALHOST_NAME = "localhost";
+ public static final String LOCALHOST_IPV4 = "127.0.0.1";
+ public static final String IPV4_ANYADDR = "0.0.0.0";
+
+ public static final Set<String> WELL_KNOWN_IPV4_ADDRESSES =
+ Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(LOCALHOST_IPV4, IPV4_ANYADDR)));
+
+ // 10.0.0.0 - 10.255.255.255
+ public static final String PRIVATE_CLASS_A_PREFIX = "10.";
+ // 172.16.0.0 - 172.31.255.255
+ public static final String PRIVATE_CLASS_B_PREFIX = "172.";
+ // 192.168.0.0 - 192.168.255.255
+ public static final String PRIVATE_CLASS_C_PREFIX = "192.168.";
+ // 100.64.0.0 - 100.127.255.255
+ public static final String CARRIER_GRADE_NAT_PREFIX = "100.";
+ // The IPv4 broadcast address
+ public static final String BROADCAST_ADDRESS = "255.255.255.255";
+
+ /** Max. number of hex groups (separated by ":") in an IPV6 address */
+ public static final int IPV6_MAX_HEX_GROUPS = 8;
+
+ /** Max. hex digits in each IPv6 group */
+ public static final int IPV6_MAX_HEX_DIGITS_PER_GROUP = 4;
+
+ public static final String IPV6_LONG_ANY_ADDRESS = "0:0:0:0:0:0:0:0";
+ public static final String IPV6_SHORT_ANY_ADDRESS = "::";
+
+ public static final String IPV6_LONG_LOCALHOST = "0:0:0:0:0:0:0:1";
+ public static final String IPV6_SHORT_LOCALHOST = "::1";
+
+ public static final Set<String> WELL_KNOWN_IPV6_ADDRESSES =
+ Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(
+ IPV6_LONG_LOCALHOST, IPV6_SHORT_LOCALHOST,
+ IPV6_LONG_ANY_ADDRESS, IPV6_SHORT_ANY_ADDRESS)));
+
+ /**
+ * A dummy placeholder that can be used instead of {@code null}s
+ */
+ public static final SshdSocketAddress LOCALHOST_ADDRESS = new SshdSocketAddress(LOCALHOST_IPV4, 0);
+
+ /**
+ * Compares {@link InetAddress}-es according to their {@link InetAddress#getHostAddress()}
+ * value case <U>insensitive</U>
+ *
+ * @see #toAddressString(InetAddress)
+ */
+ public static final Comparator<InetAddress> BY_HOST_ADDRESS = (a1, a2) -> {
+ String n1 = GenericUtils.trimToEmpty(toAddressString(a1));
+ String n2 = GenericUtils.trimToEmpty(toAddressString(a2));
+ return String.CASE_INSENSITIVE_ORDER.compare(n1, n2);
+ };
+
+ /**
+ * Compares {@link SocketAddress}-es according to their host case <U>insensitive</U>
+ * and if equals, then according to their port value (if any)
+ *
+ * @see #toAddressString(SocketAddress)
+ * @see #toAddressPort(SocketAddress)
+ */
+ public static final Comparator<SocketAddress> BY_HOST_AND_PORT = (a1, a2) -> {
+ String n1 = GenericUtils.trimToEmpty(toAddressString(a1));
+ String n2 = GenericUtils.trimToEmpty(toAddressString(a2));
+ int nRes = String.CASE_INSENSITIVE_ORDER.compare(n1, n2);
+ if (nRes != 0) {
+ return nRes;
+ }
+
+ int p1 = toAddressPort(a1);
+ int p2 = toAddressPort(a2);
+ nRes = Integer.compare(p1, p2);
+ if (nRes != 0) {
+ return nRes;
+ }
+
+ return 0;
+ };
+
+ private static final long serialVersionUID = 6461645947151952729L;
+
+ private final String hostName;
+ private final int port;
+
+ public SshdSocketAddress(int port) {
+ this(IPV4_ANYADDR, port);
+ }
+
+ public SshdSocketAddress(InetSocketAddress addr) {
+ Objects.requireNonNull(addr, "No address provided");
+
+ String host = addr.getHostString();
+ hostName = GenericUtils.isEmpty(host) ? IPV4_ANYADDR : host;
+ port = addr.getPort();
+ ValidateUtils.checkTrue(port >= 0, "Port must be >= 0: %d", port);
+ }
+
+ public SshdSocketAddress(String hostName, int port) {
+ Objects.requireNonNull(hostName, "Host name may not be null");
+ this.hostName = GenericUtils.isEmpty(hostName) ? IPV4_ANYADDR : hostName;
+
+ ValidateUtils.checkTrue(port >= 0, "Port must be >= 0: %d", port);
+ this.port = port;
+ }
+
+ public String getHostName() {
+ return hostName;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public InetSocketAddress toInetSocketAddress() {
+ return new InetSocketAddress(getHostName(), getPort());
+ }
+
+ @Override
+ public String toString() {
+ return getHostName() + ":" + getPort();
+ }
+
+ protected boolean isEquivalent(SshdSocketAddress that) {
+ if (that == null) {
+ return false;
+ } else if (that == this) {
+ return true;
+ } else {
+ return (this.getPort() == that.getPort())
+ && (GenericUtils.safeCompare(this.getHostName(), that.getHostName(), false) == 0);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
+ if (getClass() != o.getClass()) {
+ return false;
+ }
+ return isEquivalent((SshdSocketAddress) o);
+ }
+
+ @Override
+ public int hashCode() {
+ return GenericUtils.hashCode(getHostName(), Boolean.FALSE) + getPort();
+ }
+
+
+ /**
+ * Returns the first external network address assigned to this
+ * machine or null if one is not found.
+ * @return Inet4Address associated with an external interface
+ * DevNote: We actually return InetAddress here, as Inet4Addresses are final and cannot be mocked.
+ */
+ public static InetAddress getFirstExternalNetwork4Address() {
+ List<? extends InetAddress> addresses = getExternalNetwork4Addresses();
+ return (GenericUtils.size(addresses) > 0) ? addresses.get(0) : null;
+ }
+
+ /**
+ * @return a {@link List} of local network addresses which are not multicast
+ * or localhost sorted according to {@link #BY_HOST_ADDRESS}
+ */
+ public static List<InetAddress> getExternalNetwork4Addresses() {
+ List<InetAddress> addresses = new ArrayList<>();
+ try {
+ for (Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces(); (nets != null) && nets.hasMoreElements();) {
+ NetworkInterface netint = nets.nextElement();
+ /* TODO - uncomment when 1.5 compatibility no longer required
+ if (!netint.isUp()) {
+ continue; // ignore non-running interfaces
+ }
+ */
+
+ for (Enumeration<InetAddress> inetAddresses = netint.getInetAddresses(); (inetAddresses != null) && inetAddresses.hasMoreElements();) {
+ InetAddress inetAddress = inetAddresses.nextElement();
+ if (isValidHostAddress(inetAddress)) {
+ addresses.add(inetAddress);
+ }
+ }
+ }
+ } catch (SocketException e) {
+ // swallow
+ }
+
+ if (GenericUtils.size(addresses) > 1) {
+ Collections.sort(addresses, BY_HOST_ADDRESS);
+ }
+
+ return addresses;
+ }
+
+ /**
+ * @param addr The {@link InetAddress} to be verified
+ * @return <P><code>true</code> if the address is:</P></BR>
+ * <UL>
+ * <LI>Not {@code null}</LI>
+ * <LI>An {@link Inet4Address}</LI>
+ * <LI>Not link local</LI>
+ * <LI>Not a multicast</LI>
+ * <LI>Not a loopback</LI>
+ * </UL>
+ * @see InetAddress#isLinkLocalAddress()
+ * @see InetAddress#isMulticastAddress()
+ * @see InetAddress#isMulticastAddress()
+ */
+ public static boolean isValidHostAddress(InetAddress addr) {
+ if (addr == null) {
+ return false;
+ }
+
+ if (addr.isLinkLocalAddress()) {
+ return false;
+ }
+
+ if (addr.isMulticastAddress()) {
+ return false;
+ }
+
+ if (!(addr instanceof Inet4Address)) {
+ return false; // TODO add support for IPv6 - see SSHD-746
+ }
+
+ return !isLoopback(addr);
+
+ }
+
+ /**
+ * @param addr The {@link InetAddress} to be considered
+ * @return <code>true</code> if the address is a loopback one.
+ * <B>Note:</B> if {@link InetAddress#isLoopbackAddress()}
+ * returns <code>false</code> the address <U>string</U> is checked
+ * @see #toAddressString(InetAddress)
+ * @see #isLoopback(String)
+ */
+ public static boolean isLoopback(InetAddress addr) {
+ if (addr == null) {
+ return false;
+ }
+
+ if (addr.isLoopbackAddress()) {
+ return true;
+ }
+
+ String ip = toAddressString(addr);
+ return isLoopback(ip);
+ }
+
+ /**
+ * @param ip IP value to be tested
+ * @return <code>true</code> if the IP is "localhost" or
+ * "127.x.x.x".
+ */
+ public static boolean isLoopback(String ip) {
+ if (GenericUtils.isEmpty(ip)) {
+ return false;
+ }
+
+ if (LOCALHOST_NAME.equals(ip) || LOCALHOST_IPV4.equals(ip)) {
+ return true;
+ }
+
+ // TODO add support for IPv6 - see SSHD-746
+ String[] values = GenericUtils.split(ip, '.');
+ if (GenericUtils.length(values) != 4) {
+ return false;
+ }
+
+ for (int index = 0; index < values.length; index++) {
+ String val = values[index];
+ if (!isValidIPv4AddressComponent(val)) {
+ return false;
+ }
+
+ if (index == 0) {
+ int number = Integer.parseInt(val);
+ if (number != 127) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public static SshdSocketAddress toSshdSocketAddress(SocketAddress addr) {
+ if (addr == null) {
+ return null;
+ } else if (addr instanceof SshdSocketAddress) {
+ return (SshdSocketAddress) addr;
+ } else if (addr instanceof InetSocketAddress) {
+ InetSocketAddress isockAddress = (InetSocketAddress) addr;
+ return new SshdSocketAddress(isockAddress.getHostName(), isockAddress.getPort());
+ } else {
+ throw new UnsupportedOperationException("Cannot convert " + addr.getClass().getSimpleName()
+ + "=" + addr + " to " + SshdSocketAddress.class.getSimpleName());
+ }
+ }
+
+ public static String toAddressString(SocketAddress addr) {
+ if (addr == null) {
+ return null;
+ } else if (addr instanceof InetSocketAddress) {
+ return ((InetSocketAddress) addr).getHostString();
+ } else if (addr instanceof SshdSocketAddress) {
+ return ((SshdSocketAddress) addr).getHostName();
+ } else {
+ return addr.toString();
+ }
+ }
+
+ /**
+ * Attempts to resolve the port value
+ *
+ * @param addr The {@link SocketAddress} to examine
+ * @return The associated port value - negative if failed to resolve
+ */
+ public static int toAddressPort(SocketAddress addr) {
+ if (addr instanceof InetSocketAddress) {
+ return ((InetSocketAddress) addr).getPort();
+ } else if (addr instanceof SshdSocketAddress) {
+ return ((SshdSocketAddress) addr).getPort();
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * <P>Converts a {@code SocketAddress} into an {@link InetSocketAddress} if possible:</P></BR>
+ * <UL>
+ * <LI>If already an {@link InetSocketAddress} then cast it as such</LI>
+ * <LI>If an {@code SshdSocketAddress} then invoke {@link #toInetSocketAddress()}</LI>
+ * <LI>Otherwise, throw an exception</LI>
+ * </UL>
+ *
+ * @param remoteAddress The {@link SocketAddress} - ignored if {@code null}
+ * @return The {@link InetSocketAddress} instance
+ * @throws ClassCastException if argument is not already an {@code InetSocketAddress}
+ * or a {@code SshdSocketAddress}
+ */
+ public static InetSocketAddress toInetSocketAddress(SocketAddress remoteAddress) {
+ if (remoteAddress == null) {
+ return null;
+ } else if (remoteAddress instanceof InetSocketAddress) {
+ return (InetSocketAddress) remoteAddress;
+ } else if (remoteAddress instanceof SshdSocketAddress) {
+ return ((SshdSocketAddress) remoteAddress).toInetSocketAddress();
+ } else {
+ throw new ClassCastException("Unknown remote address type: " + remoteAddress);
+ }
+ }
+
+ public static String toAddressString(InetAddress addr) {
+ String ip = (addr == null) ? null : addr.toString();
+ if (GenericUtils.isEmpty(ip)) {
+ return null;
+ } else {
+ return ip.replaceAll(".*/", "");
+ }
+ }
+
+ public static boolean isIPv4Address(String addr) {
+ addr = GenericUtils.trimToEmpty(addr);
+ if (GenericUtils.isEmpty(addr)) {
+ return false;
+ }
+
+ if (WELL_KNOWN_IPV4_ADDRESSES.contains(addr)) {
+ return true;
+ }
+
+ String[] comps = GenericUtils.split(addr, '.');
+ if (GenericUtils.length(comps) != 4) {
+ return false;
+ }
+
+ for (String c : comps) {
+ if (!isValidIPv4AddressComponent(c)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks if the address is one of the allocated private blocks
+ * @param addr The address string
+ * @return {@code true} if this is one of the allocated private
+ * blocks. <B>Note:</B> it assumes that the address string is
+ * indeed an IPv4 address
+ * @see #isIPv4Address(String)
+ * @see #PRIVATE_CLASS_A_PREFIX
+ * @see #PRIVATE_CLASS_B_PREFIX
+ * @see #PRIVATE_CLASS_C_PREFIX
+ * @see <A HREF="http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces">Wiki page</A>
+ */
+ public static boolean isPrivateIPv4Address(String addr) {
+ if (GenericUtils.isEmpty(addr)) {
+ return false;
+ }
+
+ if (addr.startsWith(PRIVATE_CLASS_A_PREFIX) || addr.startsWith(PRIVATE_CLASS_C_PREFIX)) {
+ return true;
+ }
+
+ // for 172.x.x.x we need further checks
+ if (!addr.startsWith(PRIVATE_CLASS_B_PREFIX)) {
+ return false;
+ }
+
+ int nextCompPos = addr.indexOf('.', PRIVATE_CLASS_B_PREFIX.length());
+ if (nextCompPos <= PRIVATE_CLASS_B_PREFIX.length()) {
+ return false;
+ }
+
+ String value = addr.substring(PRIVATE_CLASS_B_PREFIX.length(), nextCompPos);
+ if (!isValidIPv4AddressComponent(value)) {
+ return false;
+ }
+
+ int v = Integer.parseInt(value);
+ return (v >= 16) && (v <= 31);
+ }
+
+ /**
+ * @param addr The address to be checked
+ * @return {@code true} if the address is in the 100.64.0.0/10 range
+ * @see <A HREF="http://tools.ietf.org/html/rfc6598">RFC6598</A>
+ */
+ public static boolean isCarrierGradeNatIPv4Address(String addr) {
+ if (GenericUtils.isEmpty(addr)) {
+ return false;
+ }
+
+ if (!addr.startsWith(CARRIER_GRADE_NAT_PREFIX)) {
+ return false;
+ }
+
+ int nextCompPos = addr.indexOf('.', CARRIER_GRADE_NAT_PREFIX.length());
+ if (nextCompPos <= CARRIER_GRADE_NAT_PREFIX.length()) {
+ return false;
+ }
+
+ String value = addr.substring(CARRIER_GRADE_NAT_PREFIX.length(), nextCompPos);
+ if (!isValidIPv4AddressComponent(value)) {
+ return false;
+ }
+
+ int v = Integer.parseInt(value);
+ return (v >= 64) && (v <= 127);
+ }
+
+ /**
+ * <P>Checks if the provided argument is a valid IPv4 address component:</P></BR>
+ * <UL>
+ * <LI>Not {@code null}/empty</LI>
+ * <LI>Has at most 3 <U>digits</U></LI>
+ * <LI>Its value is ≤ 255</LI>
+ * </UL>
+ * @param c The {@link CharSequence} to be validate
+ * @return {@code true} if valid IPv4 address component
+ */
+ public static boolean isValidIPv4AddressComponent(CharSequence c) {
+ if (GenericUtils.isEmpty(c) || (c.length() > 3)) {
+ return false;
+ }
+
+ char ch = c.charAt(0);
+ if ((ch < '0') || (ch > '9')) {
+ return false;
+ }
+
+ if (!NumberUtils.isIntegerNumber(c)) {
+ return false;
+ }
+
+ int v = Integer.parseInt(c.toString());
+ return (v >= 0) && (v <= 255);
+ }
+
+ // Based on org.apache.commons.validator.routines.InetAddressValidator#isValidInet6Address
+ public static boolean isIPv6Address(String address) {
+ address = GenericUtils.trimToEmpty(address);
+ if (GenericUtils.isEmpty(address)) {
+ return false;
+ }
+
+ if (WELL_KNOWN_IPV6_ADDRESSES.contains(address)) {
+ return true;
+ }
+
+ boolean containsCompressedZeroes = address.contains("::");
+ if (containsCompressedZeroes && (address.indexOf("::") != address.lastIndexOf("::"))) {
+ return false;
+ }
+
+ if (((address.indexOf(':') == 0) && (!address.startsWith("::")))
+ || (address.endsWith(":") && (!address.endsWith("::")))) {
+ return false;
+ }
+
+ String[] splitOctets = GenericUtils.split(address, ':');
+ List<String> octetList = new ArrayList<>(Arrays.asList(splitOctets));
+ if (containsCompressedZeroes) {
+ if (address.endsWith("::")) {
+ // String.split() drops ending empty segments
+ octetList.add("");
+ } else if (address.startsWith("::") && (!octetList.isEmpty())) {
+ octetList.remove(0);
+ }
+ }
+
+ int numOctests = octetList.size();
+ if (numOctests > IPV6_MAX_HEX_GROUPS) {
+ return false;
+ }
+
+ int validOctets = 0;
+ int emptyOctets = 0; // consecutive empty chunks
+ for (int index = 0; index < numOctests; index++) {
+ String octet = octetList.get(index);
+ int pos = octet.indexOf('%'); // is it a zone index
+ if (pos >= 0) {
+ // zone index must come last
+ if (index != (numOctests - 1)) {
+ return false;
+ }
+
+ octet = (pos > 0) ? octet.substring(0, pos) : "";
+ }
+
+ int octetLength = octet.length();
+ if (octetLength == 0) {
+ emptyOctets++;
+ if (emptyOctets > 1) {
+ return false;
+ }
+
+ validOctets++;
+ continue;
+ }
+
+ emptyOctets = 0;
+
+ // Is last chunk an IPv4 address?
+ if ((index == (numOctests - 1)) && (octet.indexOf('.') > 0)) {
+ if (!isIPv4Address(octet)) {
+ return false;
+ }
+ validOctets += 2;
+ continue;
+ }
+
+ if (octetLength > IPV6_MAX_HEX_DIGITS_PER_GROUP) {
+ return false;
+ }
+
+ int octetInt = 0;
+ try {
+ octetInt = Integer.parseInt(octet, 16);
+ } catch (NumberFormatException e) {
+ return false;
+ }
+
+ if ((octetInt < 0) || (octetInt > 0x000ffff)) {
+ return false;
+ }
+
+ validOctets++;
+ }
+
+ if ((validOctets > IPV6_MAX_HEX_GROUPS)
+ || ((validOctets < IPV6_MAX_HEX_GROUPS) && (!containsCompressedZeroes))) {
+ return false;
+ }
+ return true;
+ }
+}
[08/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/digest/BuiltinDigestsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/digest/BuiltinDigestsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/digest/BuiltinDigestsTest.java
deleted file mode 100644
index 8cf7adf..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/digest/BuiltinDigestsTest.java
+++ /dev/null
@@ -1,65 +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.digest;
-
-import java.lang.reflect.Field;
-import java.util.EnumSet;
-import java.util.Set;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class BuiltinDigestsTest extends BaseTestSupport {
- public BuiltinDigestsTest() {
- super();
- }
-
- @Test
- public void testFromName() {
- for (BuiltinDigests expected : BuiltinDigests.VALUES) {
- String name = expected.getName();
- BuiltinDigests actual = BuiltinDigests.fromFactoryName(name);
- assertSame(name, expected, actual);
- }
- }
-
- @Test
- public void testAllConstantsCovered() throws Exception {
- Set<BuiltinDigests> avail = EnumSet.noneOf(BuiltinDigests.class);
- Field[] fields = BuiltinDigests.Constants.class.getFields();
- for (Field f : fields) {
- String name = (String) f.get(null);
- BuiltinDigests value = BuiltinDigests.fromFactoryName(name);
- assertNotNull("No match found for " + name, value);
- assertTrue(name + " re-specified", avail.add(value));
- }
-
- assertEquals("Incomplete coverage", BuiltinDigests.VALUES, avail);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/file/root/RootedFileSystemProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/file/root/RootedFileSystemProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/common/file/root/RootedFileSystemProviderTest.java
index 1425400..86c8c93 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/file/root/RootedFileSystemProviderTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/file/root/RootedFileSystemProviderTest.java
@@ -37,8 +37,8 @@ import java.util.Objects;
import java.util.Random;
import java.util.TreeSet;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.apache.sshd.util.test.NoIoTestCase;
-import org.apache.sshd.util.test.Utils;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -67,7 +67,7 @@ public class RootedFileSystemProviderTest extends AssertableFile {
@BeforeClass
public static void initializeFileSystem() throws IOException {
Path targetFolder = Objects.requireNonNull(
- Utils.detectTargetFolder(RootedFileSystemProviderTest.class), "Failed to detect target folder").toPath();
+ CommonTestSupportUtils.detectTargetFolder(RootedFileSystemProviderTest.class), "Failed to detect target folder").toPath();
rootSandbox = FileHelper.createTestSandbox(targetFolder.resolve(TEMP_SUBFOLDER_NAME));
fileSystem = (RootedFileSystem) new RootedFileSystemProvider().newFileSystem(rootSandbox, Collections.emptyMap());
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingLoadTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingLoadTest.java b/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingLoadTest.java
index c68e445..8fbf0ce 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingLoadTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingLoadTest.java
@@ -51,9 +51,9 @@ import org.apache.sshd.common.util.net.SshdSocketAddress;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.forward.AcceptAllForwardingFilter;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.apache.sshd.util.test.JSchLogger;
import org.apache.sshd.util.test.SimpleUserInfo;
-import org.apache.sshd.util.test.Utils;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -365,7 +365,7 @@ public class PortForwardingLoadTest extends BaseTestSupport {
ss.setReuseAddress(true);
ss.bind(new InetSocketAddress((InetAddress) null, 0));
int forwardedPort = ss.getLocalPort();
- int sinkPort = Utils.getFreePort();
+ int sinkPort = CoreTestSupportUtils.getFreePort();
session.setPortForwardingR(sinkPort, TEST_LOCALHOST, forwardedPort);
final boolean started[] = new boolean[1];
started[0] = false;
@@ -493,7 +493,7 @@ public class PortForwardingLoadTest extends BaseTestSupport {
Session session = createSession();
try {
final int forwardedPort1 = session.setPortForwardingL(0, host, port);
- final int forwardedPort2 = Utils.getFreePort();
+ final int forwardedPort2 = CoreTestSupportUtils.getFreePort();
session.setPortForwardingR(forwardedPort2, TEST_LOCALHOST, forwardedPort1);
outputDebugMessage("URL: http://localhost %s", forwardedPort2);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingTest.java b/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingTest.java
index 79bc2db..3cd904f 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingTest.java
@@ -58,9 +58,9 @@ import org.apache.sshd.server.forward.AcceptAllForwardingFilter;
import org.apache.sshd.server.global.CancelTcpipForwardHandler;
import org.apache.sshd.server.global.TcpipForwardHandler;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.apache.sshd.util.test.JSchLogger;
import org.apache.sshd.util.test.SimpleUserInfo;
-import org.apache.sshd.util.test.Utils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
@@ -157,7 +157,7 @@ public class PortForwardingTest extends BaseTestSupport {
@BeforeClass
public static void setUpTestEnvironment() throws Exception {
JSchLogger.init();
- sshd = Utils.setupTestServer(PortForwardingTest.class);
+ sshd = CoreTestSupportUtils.setupTestServer(PortForwardingTest.class);
PropertyResolverUtils.updateProperty(sshd, FactoryManager.WINDOW_SIZE, 2048);
PropertyResolverUtils.updateProperty(sshd, FactoryManager.MAX_PACKET_SIZE, 256);
sshd.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
@@ -221,7 +221,7 @@ public class PortForwardingTest extends BaseTestSupport {
acceptor.bind(new InetSocketAddress(0));
echoPort = acceptor.getLocalAddress().getPort();
- client = Utils.setupTestClient(PortForwardingTest.class);
+ client = CoreTestSupportUtils.setupTestClient(PortForwardingTest.class);
client.start();
}
@@ -262,7 +262,7 @@ public class PortForwardingTest extends BaseTestSupport {
public void testRemoteForwarding() throws Exception {
Session session = createSession();
try {
- int forwardedPort = Utils.getFreePort();
+ int forwardedPort = CoreTestSupportUtils.getFreePort();
session.setPortForwardingR(forwardedPort, TEST_LOCALHOST, echoPort);
waitForForwardingRequest(TcpipForwardHandler.REQUEST, TimeUnit.SECONDS.toMillis(5L));
@@ -293,7 +293,7 @@ public class PortForwardingTest extends BaseTestSupport {
public void testRemoteForwardingSecondTimeInSameSession() throws Exception {
Session session = createSession();
try {
- int forwardedPort = Utils.getFreePort();
+ int forwardedPort = CoreTestSupportUtils.getFreePort();
session.setPortForwardingR(forwardedPort, TEST_LOCALHOST, echoPort);
waitForForwardingRequest(TcpipForwardHandler.REQUEST, TimeUnit.SECONDS.toMillis(5L));
@@ -465,7 +465,7 @@ public class PortForwardingTest extends BaseTestSupport {
public void testLocalForwarding() throws Exception {
Session session = createSession();
try {
- int forwardedPort = Utils.getFreePort();
+ int forwardedPort = CoreTestSupportUtils.getFreePort();
session.setPortForwardingL(forwardedPort, TEST_LOCALHOST, echoPort);
try (Socket s = new Socket(TEST_LOCALHOST, forwardedPort);
@@ -674,7 +674,7 @@ public class PortForwardingTest extends BaseTestSupport {
Session session = createSession();
try {
// 1. Create a Port Forward
- int forwardedPort = Utils.getFreePort();
+ int forwardedPort = CoreTestSupportUtils.getFreePort();
session.setPortForwardingR(forwardedPort, TEST_LOCALHOST, echoPort);
waitForForwardingRequest(TcpipForwardHandler.REQUEST, TimeUnit.SECONDS.toMillis(5L));
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/future/DefaultSshFutureTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/future/DefaultSshFutureTest.java b/sshd-core/src/test/java/org/apache/sshd/common/future/DefaultSshFutureTest.java
deleted file mode 100644
index 0b5ec12..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/future/DefaultSshFutureTest.java
+++ /dev/null
@@ -1,137 +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.future;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-import org.mockito.Mockito;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class DefaultSshFutureTest extends BaseTestSupport {
- public DefaultSshFutureTest() {
- super();
- }
-
- @Test
- @SuppressWarnings("rawtypes")
- public void testAwaitUninterrupted() {
- DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
- Object expected = new Object();
- new Thread() {
- @Override
- public void run() {
- try {
- Thread.sleep(TimeUnit.SECONDS.toMillis(2L));
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- future.setValue(expected);
- }
- }.start();
-
- future.awaitUninterruptibly();
- assertSame("Mismatched signalled value", expected, future.getValue());
- }
-
- @Test
- @SuppressWarnings({ "rawtypes", "unchecked" })
- public void testNotifyMultipleListeners() {
- DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
- AtomicInteger listenerCount = new AtomicInteger(0);
- Object expected = new Object();
- SshFutureListener listener = f -> {
- assertSame("Mismatched future instance", future, f);
- assertSame("Mismatched value object", expected, future.getValue());
- listenerCount.incrementAndGet();
- };
-
- int numListeners = Byte.SIZE;
- for (int index = 0; index < numListeners; index++) {
- future.addListener(listener);
- }
-
- future.setValue(expected);
- assertEquals("Mismatched listeners invocation count", numListeners, listenerCount.get());
- }
-
- @Test
- @SuppressWarnings({ "rawtypes", "unchecked" })
- public void testListenerInvokedDirectlyAfterResultSet() {
- DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
- AtomicInteger listenerCount = new AtomicInteger(0);
- Object expected = new Object();
- SshFutureListener listener = f -> {
- assertSame("Mismatched future instance", future, f);
- assertSame("Mismatched value object", expected, future.getValue());
- listenerCount.incrementAndGet();
- };
- future.setValue(expected);
-
- future.addListener(listener);
- assertEquals("Mismatched number of registered listeners", 0, future.getNumRegisteredListeners());
- assertEquals("Listener not invoked", 1, listenerCount.get());
- }
-
- @Test
- @SuppressWarnings({ "rawtypes", "unchecked" })
- public void testAddAndRemoveRegisteredListenersBeforeResultSet() {
- DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
- SshFutureListener listener = Mockito.mock(SshFutureListener.class);
- for (int index = 1; index <= Byte.SIZE; index++) {
- future.addListener(listener);
- assertEquals("Mismatched number of added listeners", index, future.getNumRegisteredListeners());
- }
-
- for (int index = future.getNumRegisteredListeners() - 1; index >= 0; index--) {
- future.removeListener(listener);
- assertEquals("Mismatched number of remaining listeners", index, future.getNumRegisteredListeners());
- }
- }
-
- @Test
- @SuppressWarnings({ "rawtypes", "unchecked" })
- public void testListenerNotRemovedIfResultSet() {
- DefaultSshFuture future = new DefaultSshFuture(getCurrentTestName(), null);
- AtomicInteger listenerCount = new AtomicInteger(0);
- Object expected = new Object();
- SshFutureListener listener = f -> {
- assertSame("Mismatched future instance", future, f);
- assertSame("Mismatched value object", expected, future.getValue());
- listenerCount.incrementAndGet();
- };
- future.addListener(listener);
- future.setValue(expected);
- assertEquals("Mismatched number of registered listeners", 1, future.getNumRegisteredListeners());
- assertEquals("Listener not invoked", 1, listenerCount.get());
-
- future.removeListener(listener);
- assertEquals("Mismatched number of remaining listeners", 1, future.getNumRegisteredListeners());
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
deleted file mode 100644
index 1c1bb9f..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/keyprovider/KeyPairProviderTest.java
+++ /dev/null
@@ -1,79 +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.keyprovider;
-
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.function.Function;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-import org.mockito.Mockito;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class KeyPairProviderTest extends BaseTestSupport {
- public KeyPairProviderTest() {
- super();
- }
-
- @Test
- public void testEmptyKeyProvider() {
- KeyPairProvider provider = KeyPairProvider.EMPTY_KEYPAIR_PROVIDER;
- assertTrue("Non empty loaded keys", GenericUtils.isEmpty(provider.loadKeys()));
- assertTrue("Non empty key type", GenericUtils.isEmpty(provider.getKeyTypes()));
-
- for (String keyType : new String[]{null, "", getCurrentTestName()}) {
- assertNull("Unexpected key-pair loaded for type='" + keyType + "'", provider.loadKey(keyType));
- }
- }
-
- @Test
- public void testMapToKeyPairProvider() {
- PublicKey pubKey = Mockito.mock(PublicKey.class);
- PrivateKey prvKey = Mockito.mock(PrivateKey.class);
- String[] testKeys = {getCurrentTestName(), getClass().getSimpleName()};
- Map<String, KeyPair> pairsMap = GenericUtils.toSortedMap(
- Arrays.asList(testKeys),
- Function.identity(),
- k -> new KeyPair(pubKey, prvKey),
- String.CASE_INSENSITIVE_ORDER);
-
- KeyPairProvider provider = MappedKeyPairProvider.MAP_TO_KEY_PAIR_PROVIDER.apply(pairsMap);
- assertEquals("Key types", pairsMap.keySet(), provider.getKeyTypes());
- assertEquals("Key pairs", pairsMap.values(), provider.loadKeys());
-
- pairsMap.forEach((keyType, expected) -> {
- KeyPair actual = provider.loadKey(keyType);
- assertSame(keyType, expected, actual);
- });
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
deleted file mode 100644
index ab9e7ae..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
+++ /dev/null
@@ -1,166 +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.mac;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.mac.BuiltinMacs.ParseResult;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-import org.mockito.Mockito;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class BuiltinMacsTest extends BaseTestSupport {
- public BuiltinMacsTest() {
- super();
- }
-
- @Test
- public void testFromName() {
- for (BuiltinMacs expected : BuiltinMacs.VALUES) {
- String name = expected.getName();
- BuiltinMacs actual = BuiltinMacs.fromFactoryName(name);
- assertSame(name, expected, actual);
- }
- }
-
- @Test
- public void testAllConstantsCovered() throws Exception {
- Set<BuiltinMacs> avail = EnumSet.noneOf(BuiltinMacs.class);
- Field[] fields = BuiltinMacs.Constants.class.getFields();
- for (Field f : fields) {
- String name = (String) f.get(null);
- BuiltinMacs value = BuiltinMacs.fromFactoryName(name);
- assertNotNull("No match found for " + name, value);
- assertTrue(name + " re-specified", avail.add(value));
- }
-
- assertEquals("Incomplete coverage", BuiltinMacs.VALUES, avail);
- }
-
- @Test
- public void testParseMacsList() {
- List<String> builtin = NamedResource.getNameList(BuiltinMacs.VALUES);
- List<String> unknown = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
- Random rnd = new Random();
- for (int index = 0; index < (builtin.size() + unknown.size()); index++) {
- Collections.shuffle(builtin, rnd);
- Collections.shuffle(unknown, rnd);
-
- List<String> weavedList = new ArrayList<>(builtin.size() + unknown.size());
- for (int bIndex = 0, uIndex = 0; (bIndex < builtin.size()) || (uIndex < unknown.size());) {
- boolean useBuiltin = false;
- if (bIndex < builtin.size()) {
- useBuiltin = uIndex >= unknown.size() || rnd.nextBoolean();
- }
-
- if (useBuiltin) {
- weavedList.add(builtin.get(bIndex));
- bIndex++;
- } else if (uIndex < unknown.size()) {
- weavedList.add(unknown.get(uIndex));
- uIndex++;
- }
- }
-
- String fullList = GenericUtils.join(weavedList, ',');
- ParseResult result = BuiltinMacs.parseMacsList(fullList);
- List<String> parsed = NamedResource.getNameList(result.getParsedFactories());
- List<String> missing = result.getUnsupportedFactories();
-
- // makes sure not only that the contents are the same but also the order
- assertListEquals(fullList + "[parsed]", builtin, parsed);
- assertListEquals(fullList + "[unsupported]", unknown, missing);
- }
- }
-
- @Test
- public void testResolveFactoryOnBuiltinValues() {
- for (MacFactory expected : BuiltinMacs.VALUES) {
- String name = expected.getName();
- MacFactory actual = BuiltinMacs.resolveFactory(name);
- assertSame(name, expected, actual);
- }
- }
-
- @Test
- public void testNotAllowedToRegisterBuiltinFactories() {
- for (MacFactory expected : BuiltinMacs.VALUES) {
- try {
- BuiltinMacs.registerExtension(expected);
- fail("Unexpected success for " + expected.getName());
- } catch (IllegalArgumentException e) {
- // expected - ignored
- }
- }
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testNotAllowedToOverrideRegisteredFactories() {
- MacFactory expected = Mockito.mock(MacFactory.class);
- Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
-
- String name = expected.getName();
- try {
- for (int index = 1; index <= Byte.SIZE; index++) {
- BuiltinMacs.registerExtension(expected);
- assertEquals("Unexpected success at attempt #" + index, 1, index);
- }
- } finally {
- BuiltinMacs.unregisterExtension(name);
- }
- }
-
- @Test
- public void testResolveFactoryOnRegisteredExtension() {
- MacFactory expected = Mockito.mock(MacFactory.class);
- Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
-
- String name = expected.getName();
- try {
- assertNull("Extension already registered", BuiltinMacs.resolveFactory(name));
- BuiltinMacs.registerExtension(expected);
-
- MacFactory actual = BuiltinMacs.resolveFactory(name);
- assertSame("Mismatched resolved instance", expected, actual);
- } finally {
- MacFactory actual = BuiltinMacs.unregisterExtension(name);
- assertSame("Mismatched unregistered instance", expected, actual);
- assertNull("Extension not un-registered", BuiltinMacs.resolveFactory(name));
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java b/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java
index 823955b..4d3bfa8 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java
@@ -32,10 +32,11 @@ import org.apache.sshd.common.channel.Channel;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.apache.sshd.util.test.JSchLogger;
import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
import org.apache.sshd.util.test.SimpleUserInfo;
-import org.apache.sshd.util.test.Utils;
import org.junit.AfterClass;
import org.junit.Assume;
import org.junit.Before;
@@ -115,8 +116,8 @@ public class MacTest extends BaseTestSupport {
public static void setupClientAndServer() throws Exception {
JSchLogger.init();
- sshd = Utils.setupTestServer(MacTest.class);
- sshd.setKeyPairProvider(Utils.createTestHostKeyProvider(MacTest.class));
+ sshd = CoreTestSupportUtils.setupTestServer(MacTest.class);
+ sshd.setKeyPairProvider(CommonTestSupportUtils.createTestHostKeyProvider(MacTest.class));
sshd.start();
port = sshd.getPort();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java
deleted file mode 100644
index 4887099..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java
+++ /dev/null
@@ -1,311 +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.mac;
-
-import java.nio.charset.StandardCharsets;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @see <A HREF="https://tools.ietf.org/html/rfc4231">RFC 4321</A>
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class MacVectorsTest extends BaseTestSupport {
- private final VectorSeed seed;
- private final Factory<? extends Mac> macFactory;
- private final byte[] expected;
-
- public MacVectorsTest(VectorSeed seed, String factoryName, String expected) {
- this.seed = Objects.requireNonNull(seed, "No seed");
- this.macFactory = ValidateUtils.checkNotNull(BuiltinMacs.fromFactoryName(factoryName), "Unknown MAC: %s", factoryName);
- this.expected = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, expected);
- }
-
- @Parameters(name = "factory={1}, expected={2}, seed={0}")
- public static Collection<Object[]> parameters() {
- List<Object[]> ret = new ArrayList<>();
- for (VectorTestData vector : Collections.unmodifiableList(
- Arrays.asList(
- ///////////////// Test Cases for HMAC-MD5 ///////////////////////
- // see https://tools.ietf.org/html/rfc2202
- new VectorTestData("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", false, "Hi There",
- Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5, // test case 1
- "9294727a3638bb1c13f48ef8158bfc9d"))),
- new VectorTestData("Jefe", "what do ya want for nothing?",
- Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5, // test case 2
- "750c783e6ab0b503eaa86e310a5db738"))),
- new VectorTestData("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", false, repeat("dd", 50), false,
- Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5, // test case 3
- "56be34521d144c88dbb8c733f0e8b3f6"))),
- /* TODO see why it fails
- new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
- Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5, // test case 4
- "697eaf0aca3a3aea3a75164746ffaa79"))),
- */
- new VectorTestData("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", false, "Test With Truncation",
- Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5, // test case 5
- "56461ef2342edc00f9bab995690efd4c"),
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5_96,
- "56461ef2342edc00f9bab995"))),
- /* TODO see why it fails
- new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
- Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5, // test case 6
- "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd"))),
- */
- /* TODO see why it fails
- new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
- Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_MD5, // test case 7
- "6f630fad67cda0ee1fb1f562db3aa53e"))),
- */
- ///////////////// Test Cases for HMAC-SHA-1 ///////////////////////
- // see https://tools.ietf.org/html/rfc2202
- new VectorTestData("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", false, "Hi There",
- Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 1
- "b617318655057264e28bc0b6fb378c8ef146be00"))),
- new VectorTestData("Jefe", "what do ya want for nothing?",
- Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 2
- "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"))),
- new VectorTestData("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", false, repeat("dd", 50), false,
- Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 3
- "125d7342b9ac11cd91a39af48aa17b4f63f175d3"))),
- /* TODO see why it fails
- new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
- Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 4
- "4c9007f4026250c6bc8414f9bf50c86c2d7235da"))),
- */
- new VectorTestData("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", false, "Test With Truncation",
- Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 5
- "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"),
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1_96,
- "4c1a03424b55e07fe7f27be1"))),
- /* TODO see why this fails
- new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
- Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 6
- "aa4ae5e15272d00e95705637ce8a3b55ed402112"))),
- */
-
- /* TODO see why it fails
- new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
- Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 7
- "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"),
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1_96,
- "4c1a03424b55e07fe7f27be1"))),
- */
-
- /* TODO see why it fails
- new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
- Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 8
- "aa4ae5e15272d00e95705637ce8a3b55ed402112"))),
- */
-
- /* TODO see why it fails
- new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
- Arrays.asList(new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 9
- "e8e99d0f45237d786d6bbaa7965c7808bbff1a91"))),
- */
-
- ///////////////// Test Cases for HMAC-SHA-2 ///////////////////////
- // see https://tools.ietf.org/html/rfc4231
- new VectorTestData(repeat("0b", 20), false, "Hi There",
- // test case 1
- Arrays.asList(
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
- "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"),
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
- "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"))),
- new VectorTestData("Jefe", "what do ya want for nothing?",
- // test case 2
- Arrays.asList(
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
- "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"),
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
- "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"))),
- new VectorTestData(repeat("aa", 20), false, repeat("dd", 50), false,
- // test case 3
- Arrays.asList(
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
- "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"),
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
- "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"))),
- new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
- // test case 4
- Arrays.asList(
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
- "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"),
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
- "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd"))),
-
- /* TODO see why it fails
- new VectorTestData(repeat("0c", 20), false, "Test With Truncation",
- Arrays.asList( // test case 5
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
- "a3b6167473100ee06e0c796c2955552b"),
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
- "415fad6271580a531d4179bc891d87a6"))),
- */
-
- /* TODO see why it fails
- new VectorTestData(repeat("aa", 131), false, "Test Using Larger Than Block-Size Key - Hash Key First",
- Arrays.asList( // test case 6
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
- "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"),
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
- "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598"))),
- */
-
- /* TODO see why it fails
- new VectorTestData(repeat("aa", 131), false, "This is a test using a larger than block-size"
- + " key and a larger than block-size data."
- + " The key needs to be hashed before being used"
- + " by the HMAC algorithm",
- Arrays.asList( // test case 7
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_256,
- "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"),
- new SimpleImmutableEntry<>(BuiltinMacs.Constants.HMAC_SHA2_512,
- "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58")))
- */
-
- // mark end
- new VectorTestData("", false, "", false, Collections.emptyList())))) {
- for (Map.Entry<String, String> tc : vector.getResults()) {
- ret.add(new Object[]{vector, tc.getKey(), tc.getValue()});
- }
- }
-
- return ret;
- }
-
- @Test
- public void testStandardVectorMac() throws Exception {
- Mac mac = macFactory.create();
- mac.init(seed.getKey());
- mac.update(seed.getData());
-
- byte[] actual = new byte[mac.getBlockSize()];
- mac.doFinal(actual);
- assertArrayEquals("Mismatched results for actual=" + BufferUtils.toHex(BufferUtils.EMPTY_HEX_SEPARATOR, actual), expected, actual);
- }
-
- private static class VectorSeed {
- private final byte[] key;
- private final String keyString;
- private final byte[] data;
- private final String dataString;
-
- VectorSeed(String key, String data) {
- this.key = key.getBytes(StandardCharsets.UTF_8);
- this.keyString = key;
- this.data = data.getBytes(StandardCharsets.UTF_8);
- this.dataString = data;
- }
-
- VectorSeed(String key, boolean useKeyString, String data) {
- this.key = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, key);
- this.keyString = useKeyString ? new String(this.key, StandardCharsets.UTF_8) : key;
- this.data = data.getBytes(StandardCharsets.UTF_8);
- this.dataString = data;
- }
-
- VectorSeed(String key, boolean useKeyString, String data, boolean useDataString) {
- this.key = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, key);
- this.keyString = useKeyString ? new String(this.key, StandardCharsets.UTF_8) : key;
- this.data = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, data);
- this.dataString = useDataString ? new String(this.data, StandardCharsets.UTF_8) : data;
- }
-
- public byte[] getKey() {
- return key.clone(); // clone to avoid inadvertent change
- }
-
- public String getKeyString() {
- return keyString;
- }
-
- public byte[] getData() {
- return data.clone(); // clone to avoid inadvertent change
- }
-
- public String getDataString() {
- return dataString;
- }
-
- @Override
- public String toString() {
- return "key=" + trimToLength(getKeyString(), 32) + ", data=" + trimToLength(getDataString(), 32);
- }
-
- private static CharSequence trimToLength(CharSequence csq, int maxLen) {
- if (GenericUtils.length(csq) <= maxLen) {
- return csq;
- }
-
- return csq.subSequence(0, maxLen) + "...";
- }
- }
-
- private static class VectorTestData extends VectorSeed {
- private final Collection<Map.Entry<String, String>> results;
-
- VectorTestData(String key, String data, Collection<Map.Entry<String, String>> results) {
- super(key, data);
- this.results = results;
- }
-
- VectorTestData(String key, boolean useKeyString, String data, Collection<Map.Entry<String, String>> results) {
- super(key, useKeyString, data);
- this.results = results;
- }
-
- VectorTestData(String key, boolean useKeyString, String data, boolean useDataString, Collection<Map.Entry<String, String>> results) {
- super(key, useKeyString, data, useDataString);
- this.results = results;
- }
-
- public Collection<Map.Entry<String, String>> getResults() {
- return results;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/random/RandomFactoryTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/random/RandomFactoryTest.java b/sshd-core/src/test/java/org/apache/sshd/common/random/RandomFactoryTest.java
deleted file mode 100644
index f3f1634..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/random/RandomFactoryTest.java
+++ /dev/null
@@ -1,83 +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.random;
-
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class RandomFactoryTest extends BaseTestSupport {
- private final RandomFactory factory;
-
- public RandomFactoryTest(RandomFactory factory) {
- this.factory = factory;
- }
-
- @Parameters(name = "type={0}")
- public static Collection<Object[]> parameters() {
- Collection<RandomFactory> testCases = new LinkedList<>();
- testCases.add(JceRandomFactory.INSTANCE);
- if (SecurityUtils.isBouncyCastleRegistered()) {
- testCases.add(SecurityUtils.getRandomFactory());
- } else {
- System.out.println("Skip BouncyCastleRandomFactory - unsupported");
- }
-
- return parameterize(testCases);
- }
-
- @Test
- public void testRandomFactory() {
- Assume.assumeTrue("Skip unsupported factory: " + factory.getName(), factory.isSupported());
- long t = testRandom(factory.create());
- System.out.println(factory.getName() + " duration: " + t + " " + TimeUnit.MICROSECONDS);
- }
-
- // returns duration in microseconds
- private static long testRandom(Random random) {
- byte[] bytes = new byte[32];
- long l0 = System.nanoTime();
- for (int i = 0; i < 1000; i++) {
- random.fill(bytes, 8, 16);
- }
- long l1 = System.nanoTime();
- return TimeUnit.NANOSECONDS.toMicros(l1 - l0);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/session/helpers/AbstractSessionTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/session/helpers/AbstractSessionTest.java b/sshd-core/src/test/java/org/apache/sshd/common/session/helpers/AbstractSessionTest.java
index 83a183e..4c56d14 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/session/helpers/AbstractSessionTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/session/helpers/AbstractSessionTest.java
@@ -406,7 +406,7 @@ public class AbstractSessionTest extends BaseTestSupport {
public static class MySession extends AbstractSession {
public MySession() {
- super(true, org.apache.sshd.util.test.Utils.setupTestServer(AbstractSessionTest.class), new MyIoSession());
+ super(true, org.apache.sshd.util.test.CoreTestSupportUtils.setupTestServer(AbstractSessionTest.class), new MyIoSession());
}
@Override
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
deleted file mode 100644
index 458abfc..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
+++ /dev/null
@@ -1,149 +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.signature;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Random;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.signature.BuiltinSignatures.ParseResult;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-import org.mockito.Mockito;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class BuiltinSignaturesTest extends BaseTestSupport {
- public BuiltinSignaturesTest() {
- super();
- }
-
- @Test
- public void testFromName() {
- for (BuiltinSignatures expected : BuiltinSignatures.VALUES) {
- String name = expected.getName();
- BuiltinSignatures actual = BuiltinSignatures.fromFactoryName(name);
- assertSame(name, expected, actual);
- }
- }
-
- @Test
- public void testParseSignaturesList() {
- List<String> builtin = NamedResource.getNameList(BuiltinSignatures.VALUES);
- List<String> unknown = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
- Random rnd = new Random();
- for (int index = 0; index < (builtin.size() + unknown.size()); index++) {
- Collections.shuffle(builtin, rnd);
- Collections.shuffle(unknown, rnd);
-
- List<String> weavedList = new ArrayList<>(builtin.size() + unknown.size());
- for (int bIndex = 0, uIndex = 0; (bIndex < builtin.size()) || (uIndex < unknown.size());) {
- boolean useBuiltin = false;
- if (bIndex < builtin.size()) {
- useBuiltin = uIndex >= unknown.size() || rnd.nextBoolean();
- }
-
- if (useBuiltin) {
- weavedList.add(builtin.get(bIndex));
- bIndex++;
- } else if (uIndex < unknown.size()) {
- weavedList.add(unknown.get(uIndex));
- uIndex++;
- }
- }
-
- String fullList = GenericUtils.join(weavedList, ',');
- ParseResult result = BuiltinSignatures.parseSignatureList(fullList);
- List<String> parsed = NamedResource.getNameList(result.getParsedFactories());
- List<String> missing = result.getUnsupportedFactories();
-
- // makes sure not only that the contents are the same but also the order
- assertListEquals(fullList + "[parsed]", builtin, parsed);
- assertListEquals(fullList + "[unsupported]", unknown, missing);
- }
- }
-
- @Test
- public void testResolveFactoryOnBuiltinValues() {
- for (SignatureFactory expected : BuiltinSignatures.VALUES) {
- String name = expected.getName();
- SignatureFactory actual = BuiltinSignatures.resolveFactory(name);
- assertSame(name, expected, actual);
- }
- }
-
- @Test
- public void testNotAllowedToRegisterBuiltinFactories() {
- for (SignatureFactory expected : BuiltinSignatures.VALUES) {
- try {
- BuiltinSignatures.registerExtension(expected);
- fail("Unexpected success for " + expected.getName());
- } catch (IllegalArgumentException e) {
- // expected - ignored
- }
- }
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testNotAllowedToOverrideRegisteredFactories() {
- SignatureFactory expected = Mockito.mock(SignatureFactory.class);
- Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
-
- String name = expected.getName();
- try {
- for (int index = 1; index <= Byte.SIZE; index++) {
- BuiltinSignatures.registerExtension(expected);
- assertEquals("Unexpected success at attempt #" + index, 1, index);
- }
- } finally {
- BuiltinSignatures.unregisterExtension(name);
- }
- }
-
- @Test
- public void testResolveFactoryOnRegisteredExtension() {
- SignatureFactory expected = Mockito.mock(SignatureFactory.class);
- Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
-
- String name = expected.getName();
- try {
- assertNull("Extension already registered", BuiltinSignatures.resolveFactory(name));
- BuiltinSignatures.registerExtension(expected);
-
- SignatureFactory actual = BuiltinSignatures.resolveFactory(name);
- assertSame("Mismatched resolved instance", expected, actual);
- } finally {
- SignatureFactory actual = BuiltinSignatures.unregisterExtension(name);
- assertSame("Mismatched unregistered instance", expected, actual);
- assertNull("Extension not un-registered", BuiltinSignatures.resolveFactory(name));
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java
deleted file mode 100644
index 0998bbd..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java
+++ /dev/null
@@ -1,109 +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.signature;
-
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.spec.DSAPublicKeySpec;
-
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class SignatureDSATest extends BaseTestSupport {
- public SignatureDSATest() {
- super();
- }
-
- @Test
- public void testTooShortSignature() throws Exception {
- KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
- SignatureDSA signatureDSA = new SignatureDSA(KeyUtils.DSS_ALGORITHM) {
- @Override
- protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
- return java.security.Signature.getInstance(algo);
-
- }
- };
-
- byte[] y = new byte[] {
- 0, -92, 59, 5, 72, 124, 101, 124, -18, 114, 7, 100, 98, -61, 73, -104,
- 120, -98, 54, 118, 17, -62, 91, -110, 29, 98, 50, -101, -41, 99, -116,
- 101, 107, -123, 124, -97, 62, 119, 88, -109, -110, -1, 109, 119, -51,
- 69, -98, -105, 2, -69, -121, -82, -118, 23, -6, 96, -61, -65, 102, -58,
- -74, 32, -104, 116, -6, -35, -83, -10, -88, -68, 106, -112, 72, -2, 35,
- 38, 15, -11, -22, 30, -114, -46, -47, -18, -17, -71, 24, -25, 28, 13, 29,
- -40, 101, 18, 81, 45, -120, -67, -53, -41, 11, 50, -89, -33, 50, 54, -14,
- -91, -35, 12, -42, 13, -84, -19, 100, -3, -85, -18, 74, 99, -49, 64, -49,
- 51, -83, -82, -127, 116, 64 };
- byte[] p = new byte[] {
- 0, -3, 127, 83, -127, 29, 117, 18, 41, 82, -33, 74, -100, 46, -20, -28,
- -25, -10, 17, -73, 82, 60, -17, 68, 0, -61, 30, 63, -128, -74, 81, 38,
- 105, 69, 93, 64, 34, 81, -5, 89, 61, -115, 88, -6, -65, -59, -11, -70,
- 48, -10, -53, -101, 85, 108, -41, -127, 59, -128, 29, 52, 111, -14, 102,
- 96, -73, 107, -103, 80, -91, -92, -97, -97, -24, 4, 123, 16, 34, -62, 79,
- -69, -87, -41, -2, -73, -58, 27, -8, 59, 87, -25, -58, -88, -90, 21, 15, 4,
- -5, -125, -10, -45, -59, 30, -61, 2, 53, 84, 19, 90, 22, -111, 50, -10, 117,
- -13, -82, 43, 97, -41, 42, -17, -14, 34, 3, 25, -99, -47, 72, 1, -57 };
- byte[] q = new byte[] {
- 0, -105, 96, 80, -113, 21, 35, 11, -52, -78, -110, -71, -126, -94, -21,
- -124, 11, -16, 88, 28, -11 };
- byte[] g = new byte[] {
- 0, -9, -31, -96, -123, -42, -101, 61, -34, -53, -68, -85, 92, 54, -72, 87,
- -71, 121, -108, -81, -69, -6, 58, -22, -126, -7, 87, 76, 11, 61, 7, -126,
- 103, 81, 89, 87, -114, -70, -44, 89, 79, -26, 113, 7, 16, -127, -128, -76,
- 73, 22, 113, 35, -24, 76, 40, 22, 19, -73, -49, 9, 50, -116, -56, -90, -31,
- 60, 22, 122, -117, 84, 124, -115, 40, -32, -93, -82, 30, 43, -77, -90, 117,
- -111, 110, -93, 127, 11, -6, 33, 53, 98, -15, -5, 98, 122, 1, 36, 59, -52,
- -92, -15, -66, -88, 81, -112, -119, -88, -125, -33, -31, 90, -27, -97, 6,
- -110, -117, 102, 94, -128, 123, 85, 37, 100, 1, 76, 59, -2, -49, 73, 42 };
-
- BigInteger bigY = new BigInteger(y);
- BigInteger bigP = new BigInteger(p);
- BigInteger bigQ = new BigInteger(q);
- BigInteger bigG = new BigInteger(g);
-
- DSAPublicKeySpec dsaPublicKey = new DSAPublicKeySpec(bigY, bigP, bigQ, bigG);
- signatureDSA.initVerifier(kf.generatePublic(dsaPublicKey));
- byte[] h = new byte[] {
- -4, 111, -103, 111, 72, -106, 105, -19, 81, -123, 84, -13, -40, -53, -3,
- -97, -8, 43, -22, -2, -23, -15, 28, 116, -63, 96, -79, -127, -84, 63, -6, -94 };
- signatureDSA.update(h);
-
- byte[] sig_of_h = new byte[] {
- 0, 0, 0, 7, 115, 115, 104, 45, 100, 115, 115, 0, 0, 0, 40, 0, 79,
- 84, 118, -50, 11, -117, -112, 52, -25, -78, -50, -20, 6, -69, -26,
- 7, 90, -34, -124, 80, 76, -32, -23, -8, 43, 38, -48, -89, -17, -60,
- -1, -78, 112, -88, 14, -39, -78, -98, -80 };
- boolean verified = signatureDSA.verify(sig_of_h);
-
- assertTrue(verified);
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java
index 8b2943b..0733967 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureFactoriesTest.java
@@ -43,8 +43,8 @@ import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.security.SecurityUtils;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.Utils;
import org.junit.AfterClass;
import org.junit.Assume;
import org.junit.BeforeClass;
@@ -122,11 +122,11 @@ public class SignatureFactoriesTest extends BaseTestSupport implements OptionalF
@BeforeClass
public static void setupClientAndServer() throws Exception {
- sshd = Utils.setupTestServer(SignatureFactoriesTest.class);
+ sshd = CoreTestSupportUtils.setupTestServer(SignatureFactoriesTest.class);
sshd.start();
port = sshd.getPort();
- client = Utils.setupTestClient(SignatureFactoriesTest.class);
+ client = CoreTestSupportUtils.setupTestClient(SignatureFactoriesTest.class);
client.start();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
deleted file mode 100644
index 6f0cb6e..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
+++ /dev/null
@@ -1,119 +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.signature;
-
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.Provider;
-import java.security.PublicKey;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.Base64;
-import java.util.Map;
-
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.junit.BeforeClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class SignatureRSATest extends BaseTestSupport {
- private static final Base64.Decoder B64_DECODER = Base64.getDecoder();
- @SuppressWarnings("checkstyle:linelength")
- private static final byte[] TEST_MSG =
- B64_DECODER.decode("AAAAFPHgK1MeV9zNnok3pwNJhCd8SONqMgAAAAlidWlsZHVzZXIAAAAOc3NoLWNvbm5lY3Rpb24AAAAJcHVibGlja2V5AQAAAAdzc2gtcnNhAAABFQAAAAdzc2gtcnNhAAAAASMAAAEBAMs9HO/NH/Now+6fSnESebaG4wzaYQWA1b/q1TGV1wHNtCg9fGFGVSKs0VxKF4cfVyrSLtgLjnlXQTn+Lm7xiYKGbBbsTQWOqEDaBVBsRbAkxIkpuvr6/EBxwrtDbKmSQYTJZVJSD2bZRYjGsR9gpZXPorOOKFd5EPCMHXsqnhp2hidTGH7cK6RuLk7MNnPISsY0Nbx8/ZvikiPROGcoTZ8bzUv4IaLr3veW6epSeQem8tJqhnrpTHhbLU99zf045M0Gsnk/azjjlBM+qrHZ5FNdC1kowJnLtf2Oy/rUQNpkGJtcBPT8xvreV0wLsn9t3hSxzsc0+VkDNTQRlfU+o3M=");
- @SuppressWarnings("checkstyle:linelength")
- private static final byte[] TEST_SIGNATURE =
- B64_DECODER.decode("AAAAB3NzaC1yc2EAAAD/+Ntnf4qfr2J1voDS6I+u3VRjtMn+LdWJsAZfkLDxRkK1rQxP7QAjLdNqpT4CkWHp8dtoTGFlBFt6NieNJCMTA2KSOxJMZKsX7e/lHkh7C+vhQvJ9eLTKWjCxSFUrcM0NvFhmwbRCffwXSHvAKak4wbmofxQMpd+G4jZkNMz5kGpmeICBcNjRLPb7oXzuGr/g4x/3ge5Qaawqrg/gcZr/sKN6SdE8SszgKYO0SB320N4gcUoShVdLYr9uwdJ+kJoobfkUK6Or171JCctP/cu2nM79lDqVnJw/2jOG8OnTc8zRDXAh0RKoR5rOU8cOHm0Ls2MATsFdnyRU5FGUxqZ+");
- private static PublicKey testKey;
-
- public SignatureRSATest() {
- super();
- }
-
- @BeforeClass
- public static void initializeTestKey() throws GeneralSecurityException {
- byte[] exp = B64_DECODER.decode("Iw==");
- @SuppressWarnings("checkstyle:linelength")
- byte[] mod = B64_DECODER.decode("AMs9HO/NH/Now+6fSnESebaG4wzaYQWA1b/q1TGV1wHNtCg9fGFGVSKs0VxKF4cfVyrSLtgLjnlXQTn+Lm7xiYKGbBbsTQWOqEDaBVBsRbAkxIkpuvr6/EBxwrtDbKmSQYTJZVJSD2bZRYjGsR9gpZXPorOOKFd5EPCMHXsqnhp2hidTGH7cK6RuLk7MNnPISsY0Nbx8/ZvikiPROGcoTZ8bzUv4IaLr3veW6epSeQem8tJqhnrpTHhbLU99zf045M0Gsnk/azjjlBM+qrHZ5FNdC1kowJnLtf2Oy/rUQNpkGJtcBPT8xvreV0wLsn9t3hSxzsc0+VkDNTQRlfU+o3M=");
- KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
- testKey = kf.generatePublic(new RSAPublicKeySpec(new BigInteger(mod), new BigInteger(exp)));
- }
-
- @Test // see SSHD-642
- public void testLeadingZeroesBC() throws Throwable {
- testLeadingZeroes(new Factory<SignatureRSA>() {
- @Override
- public SignatureRSA create() {
- return new SignatureRSA() {
- @Override
- protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
- assertFalse("Signature not initialized for verification", forSigning);
- java.security.Signature signature = super.doInitSignature(algo, forSigning);
- if (SecurityUtils.isBouncyCastleRegistered()) {
- Provider provider = signature.getProvider();
- String name = provider.getName();
- assertEquals("Mismatched BC provider name", SecurityUtils.BOUNCY_CASTLE, name);
- }
- return signature;
- }
- };
- }
- });
- }
-
- @Test // see SSHD-642
- public void testLeadingZeroesJCE() throws Throwable {
- testLeadingZeroes(() -> new SignatureRSA() {
- @Override
- protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
- assertFalse("Signature not initialized for verification", forSigning);
- java.security.Signature signature = java.security.Signature.getInstance(algo);
- Provider provider = signature.getProvider();
- String name = provider.getName();
- assertNotEquals("BC provider used although not required", SecurityUtils.BOUNCY_CASTLE, name);
- return signature;
- }
- });
- }
-
- private void testLeadingZeroes(Factory<? extends SignatureRSA> factory) throws Exception {
- SignatureRSA rsa = factory.create();
- rsa.initVerifier(testKey);
-
- int vSize = rsa.getVerifierSignatureSize();
- assertTrue("Verifier signature size not initialized", vSize > 0);
-
- // make sure padding is required
- Map.Entry<String, byte[]> encoding = rsa.extractEncodedSignature(TEST_SIGNATURE);
- assertNotNull("Signature is not encoded", encoding);
- byte[] data = encoding.getValue();
- assertTrue("Signature data size (" + data.length + ") not below verifier size (" + vSize + ")", data.length < vSize);
-
- rsa.update(TEST_MSG);
- assertTrue("Failed to verify", rsa.verify(TEST_SIGNATURE));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java
deleted file mode 100644
index 180e997..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java
+++ /dev/null
@@ -1,79 +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.signature;
-
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-
-/**
- * A "scratch-pad" class for testing signatures related code during development
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SignaturesDevelopment extends BaseTestSupport {
- public SignaturesDevelopment() {
- super();
- }
-
- public static void testSignatureFactory(
- SignatureFactory factory, KeyPair kp, byte[] data, boolean generateSignature, byte[] signature)
- throws Exception {
- Signature signer = factory.create();
- if (generateSignature) {
- signer.initSigner(kp.getPrivate());
- signer.update(data);
- signature = signer.sign();
- System.out.append('\t').append("Signature: ").println(BufferUtils.toHex(':', signature));
- } else {
- signer.initVerifier(kp.getPublic());
- signer.update(data);
- if (signer.verify(signature)) {
- System.out.append('\t').println("Valid signature");
- } else {
- System.err.append('\t').println("Invalid signature");
- }
- }
- }
-
- //////////////////////////////////////////////////////////////////////////
-
- // args[0]=signatureName, args[1]=publicKey, args[2]=privateKey, args[3]=sign/verify, args[4]=data, args[5]=signature(if verify required)
- public static void main(String[] args) throws Exception {
- SignatureFactory factory = BuiltinSignatures.resolveFactory(args[0]);
- // TODO recover public/private keys according to factory name
- byte[] publicKey = BufferUtils.decodeHex(':', args[1]);
- PublicKey pubKey = EdDSASecurityProviderUtils.generateEDDSAPublicKey(publicKey);
- byte[] privateKey = BufferUtils.decodeHex(':', args[2]);
- PrivateKey prvKey = EdDSASecurityProviderUtils.generateEDDSAPrivateKey(privateKey);
- String op = args[3];
- byte[] data = BufferUtils.decodeHex(':', args[4]);
- byte[] signature = GenericUtils.EMPTY_BYTE_ARRAY;
- if ("verify".equalsIgnoreCase(op)) {
- signature = BufferUtils.decodeHex(':', args[5]);
- }
-
- testSignatureFactory(factory, new KeyPair(pubKey, prvKey), data, "sign".equalsIgnoreCase(op), signature);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
deleted file mode 100644
index 3732f8a..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
+++ /dev/null
@@ -1,149 +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.util;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EventListener;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class EventListenerUtilsTest extends BaseTestSupport {
- public EventListenerUtilsTest() {
- super();
- }
-
- @Test
- public void testProxyWrapper() {
- List<ProxyListenerImpl> impls = new ArrayList<>();
- for (int index = 0; index < Byte.SIZE; index++) {
- impls.add(new ProxyListenerImpl());
- }
-
- ProxyListener listener = EventListenerUtils.proxyWrapper(ProxyListener.class, impls);
- String expStr = getCurrentTestName();
- Number expNum = System.currentTimeMillis();
- listener.callMeWithString(expStr);
- listener.callMeWithNumber(expNum);
-
- for (int index = 0; index < impls.size(); index++) {
- ProxyListenerImpl l = impls.get(index);
- assertSame("Mismatched string at listener #" + index, expStr, l.getStringValue());
- assertSame("Mismatched number at listener #" + index, expNum, l.getNumberValue());
- }
- }
-
- @Test
- public void testListenerInstanceComparatorOnProxy() {
- Comparator<? super EventListener> comparator = EventListenerUtils.LISTENER_INSTANCE_COMPARATOR;
- ProxyListener p1 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
- assertEquals("Mismatched self reference comparison", 0, comparator.compare(p1, p1));
-
- EventListener l = new EventListener() { /* nothing extra */ };
- assertEquals("Mismatched proxy vs. non-proxy result", 1, Integer.signum(comparator.compare(p1, l)));
- assertEquals("Mismatched non-proxy vs. proxy result", -1, Integer.signum(comparator.compare(l, p1)));
-
- ProxyListener p2 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
- int p1vsp2 = Integer.signum(comparator.compare(p1, p2));
- assertNotEquals("Mismatched p1 vs. p2 comparison", 0, p1vsp2);
- assertEquals("Mismatched p2 vs. p1 comparison result", 0 - p1vsp2, Integer.signum(comparator.compare(p2, p1)));
- }
-
- @Test
- public void testListenerInstanceComparatorOnNonProxy() {
- Comparator<? super EventListener> comparator = EventListenerUtils.LISTENER_INSTANCE_COMPARATOR;
- EventListener l1 = new EventListener() { /* nothing extra */ };
- assertEquals("Mismatched self reference comparison", 0, comparator.compare(l1, l1));
-
- EventListener l2 = new EventListener() { /* nothing extra */ };
- int l1vsl2 = Integer.signum(comparator.compare(l1, l2));
- assertNotEquals("Mismatched l1 vs. l2 comparison result", 0, l1vsl2);
- assertEquals("Mismatched l2 vs. l1 comparison result", 0 - l1vsl2, Integer.signum(comparator.compare(l2, l1)));
- }
-
- @Test
- public void testSynchronizedListenersSetOnProxies() {
- ProxyListener p1 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
- Set<ProxyListener> s = EventListenerUtils.synchronizedListenersSet();
- for (int index = 1; index <= Byte.SIZE; index++) {
- boolean modified = s.add(p1);
- assertEquals("Mismatched p1 modification indicator at attempt #" + index, index == 1, modified);
- assertEquals("Mismatched p1 set size at attempt #" + index, 1, s.size());
- }
-
- ProxyListener p2 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
- for (int index = 1; index <= Byte.SIZE; index++) {
- boolean modified = s.add(p2);
- assertEquals("Mismatched p2 modification indicator at attempt #" + index, index == 1, modified);
- assertEquals("Mismatched p2 set size at attempt #" + index, 2, s.size());
- }
-
- assertTrue("Failed to remove p1", s.remove(p1));
- assertEquals("Mismatched post p1-remove size", 1, s.size());
- assertTrue("Failed to remove p2", s.remove(p2));
- assertEquals("Mismatched post p2-remove size", 0, s.size());
- }
-
- interface ProxyListener extends SshdEventListener {
- void callMeWithString(String s);
-
- void callMeWithNumber(Number n);
- }
-
- static class ProxyListenerImpl implements ProxyListener {
- private String strValue;
- private Number numValue;
-
- ProxyListenerImpl() {
- super();
- }
-
- public String getStringValue() {
- return strValue;
- }
-
- @Override
- public void callMeWithString(String s) {
- strValue = s;
- }
-
- public Number getNumberValue() {
- return numValue;
- }
-
- @Override
- public void callMeWithNumber(Number n) {
- numValue = n;
- }
- }
-}
[48/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
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 "org.apache.sshd" 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 "pure" 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
+ }
+}
[06/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java
deleted file mode 100644
index 193aabd..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseReaderTest.java
+++ /dev/null
@@ -1,95 +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.util.io;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Date;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class NoCloseReaderTest extends BaseTestSupport {
- public NoCloseReaderTest() {
- super();
- }
-
- @Test
- public void testCanKeepReadingAfterClose() throws IOException {
- String expected = getClass().getName() + "#" + getCurrentTestName() + "@" + new Date();
- Path dir = createTempClassFolder();
- Path file = Files.write(dir.resolve(getCurrentTestName() + ".txt"), expected.getBytes(StandardCharsets.UTF_8));
- try (InputStream fileStream = Files.newInputStream(file);
- Reader rdr = new InputStreamReader(fileStream, StandardCharsets.UTF_8);
- Reader shielded = new NoCloseReader(rdr)) {
- int index = 0;
-
- int availLen = expected.length();
- for (; index < (availLen / 2); index++) {
- shielded.close();
-
- int readValue = shielded.read();
- if (readValue == -1) {
- fail("Premature EOF after shield read of " + index + " bytes");
- }
-
- char expValue = expected.charAt(index);
- char actValue = (char) (readValue & 0xFFFF);
- if (expValue != actValue) {
- fail("Mismatched shielded read value after " + index + " bytes");
- }
- }
-
- for (; index < availLen; index++) {
- int readValue = rdr.read();
- if (readValue == -1) {
- fail("Premature EOF after original read of " + index + " bytes");
- }
-
- char expValue = expected.charAt(index);
- char actValue = (char) (readValue & 0xFFFF);
- if (expValue != actValue) {
- fail("Mismatched original read value after " + index + " bytes");
- }
- }
-
- int readValue = shielded.read();
- assertEquals("Shielded EOF not signalled", -1, readValue);
-
- readValue = rdr.read();
- assertEquals("Original EOF not signalled", -1, readValue);
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java
deleted file mode 100644
index efc6a91..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseWriterTest.java
+++ /dev/null
@@ -1,73 +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.util.io;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Date;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class NoCloseWriterTest extends BaseTestSupport {
- public NoCloseWriterTest() {
- super();
- }
-
- @Test
- public void testCanKeepWritingAfterClose() throws IOException {
- Path dir = createTempClassFolder();
- Path file = dir.resolve(getCurrentTestName() + ".txt");
- Files.deleteIfExists(file);
-
- String expected = getClass().getName() + "#" + getCurrentTestName() + "@" + new Date();
- try (OutputStream fileStream = Files.newOutputStream(file);
- Writer w = new OutputStreamWriter(fileStream, StandardCharsets.UTF_8);
- Writer shielded = new NoCloseWriter(w)) {
- int index = 0;
- int availLen = expected.length();
- for (; index < (availLen / 2); index++) {
- shielded.close();
- shielded.write(expected.charAt(index));
- }
-
- w.write(expected, index, availLen - index);
- }
-
- byte[] actualBytes = Files.readAllBytes(file);
- String actual = new String(actualBytes, StandardCharsets.UTF_8);
- assertEquals(expected, actual);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java
deleted file mode 100644
index bc97b9c..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NullInputStreamTest.java
+++ /dev/null
@@ -1,118 +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.util.io;
-
-import java.io.EOFException;
-import java.io.IOException;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class NullInputStreamTest extends BaseTestSupport {
- private static final NullInputStream INSTANCE = new NullInputStream();
-
- public NullInputStreamTest() {
- super();
- }
-
- @Test
- public void testReadOneChar() throws IOException {
- assertEquals(-1, INSTANCE.read());
- }
-
- @Test
- public void testReadFullBuffer() throws IOException {
- assertEquals(-1, INSTANCE.read(new byte[Byte.SIZE]));
- }
-
- @Test
- public void testReadPartialBuffer() throws IOException {
- byte[] buf = new byte[Byte.SIZE];
- assertEquals(-1, INSTANCE.read(buf, buf.length / 2, (buf.length / 2) - 1));
- }
-
- @Test
- public void testSkip() throws IOException {
- assertEquals(0L, INSTANCE.skip(Long.SIZE));
- }
-
- @Test
- public void testAvailable() throws IOException {
- assertEquals(0, INSTANCE.available());
- }
-
- @Test
- public void testNotAllowedToAccessAfterClose() throws IOException {
- NullInputStream stream = new NullInputStream();
- stream.close();
- assertFalse("Stream not marked as closed", stream.isOpen());
-
- try {
- int nRead = stream.read();
- fail("Unexpected single byte read: " + nRead);
- } catch (EOFException e) {
- // expected
- }
-
- byte[] buf = new byte[Byte.SIZE];
- try {
- int nRead = stream.read(buf);
- fail("Unexpected full buffer read: " + nRead);
- } catch (EOFException e) {
- // expected
- }
-
- try {
- int nRead = stream.read(buf, buf.length / 2, (buf.length / 2) - 1);
- fail("Unexpected partial buffer read: " + nRead);
- } catch (EOFException e) {
- // expected
- }
-
- try {
- long skip = stream.skip(Long.SIZE);
- fail("Unexpected skip result: " + skip);
- } catch (EOFException e) {
- // expected
- }
-
- try {
- int nRead = stream.available();
- fail("Unexpected available count: " + nRead);
- } catch (IOException e) {
- // expected
- }
- try {
- stream.reset();
- fail("Unexpected reset success");
- } catch (EOFException e) {
- // expected
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java
deleted file mode 100644
index b6a0230..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NullOutputStreamTest.java
+++ /dev/null
@@ -1,80 +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.util.io;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.util.Arrays;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class NullOutputStreamTest extends BaseTestSupport {
- public NullOutputStreamTest() {
- super();
- }
-
- @Test
- public void testNoAccessAllowedAfterClose() throws IOException {
- NullOutputStream stream = new NullOutputStream();
- stream.close();
- assertFalse("Stream not marked as closed", stream.isOpen());
-
- try {
- stream.write('a');
- fail("Unexpected single value write success");
- } catch (EOFException e) {
- // expected
- }
-
- byte[] buf = new byte[Byte.SIZE];
- try {
- Arrays.fill(buf, (byte) 0x41);
- stream.write(buf);
- fail("Unexpected full buffer write success");
- } catch (EOFException e) {
- // expected
- }
-
- try {
- Arrays.fill(buf, (byte) 0x42);
- stream.write(buf, buf.length / 2, (buf.length / 2) - 1);
- fail("Unexpected partial buffer write success");
- } catch (EOFException e) {
- // expected
- }
-
- try {
- stream.flush();
- fail("Unexpected flush success");
- } catch (EOFException e) {
- // expected
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java
deleted file mode 100644
index 04bcb0e..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java
+++ /dev/null
@@ -1,67 +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.util.io.der;
-
-import java.util.List;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class ASN1ClassTest extends BaseTestSupport {
- private final ASN1Class expected;
-
- public ASN1ClassTest(ASN1Class expected) {
- this.expected = expected;
- }
-
- @Parameters(name = "{0}")
- public static List<Object[]> parameters() {
- return parameterize(ASN1Class.VALUES);
- }
-
- @Test
- public void testFromName() {
- String name = expected.name();
- for (int index = 1, count = name.length(); index <= count; index++) {
- assertSame(name, expected, ASN1Class.fromName(name));
- name = shuffleCase(name);
- }
- }
-
- @Test // NOTE: this also tests "fromTypeValue" since "fromDERValue" invokes it
- public void testFromDERValue() {
- assertSame(expected, ASN1Class.fromDERValue((expected.getClassValue() << 6) & 0xFF));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java
deleted file mode 100644
index 82b9611..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java
+++ /dev/null
@@ -1,67 +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.util.io.der;
-
-import java.util.List;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class ASN1TypeTest extends BaseTestSupport {
- private final ASN1Type expected;
-
- public ASN1TypeTest(ASN1Type expected) {
- this.expected = expected;
- }
-
- @Parameters(name = "{0}")
- public static List<Object[]> parameters() {
- return parameterize(ASN1Type.VALUES);
- }
-
- @Test
- public void testFromName() {
- String name = expected.name();
- for (int index = 1, count = name.length(); index <= count; index++) {
- assertSame(name, expected, ASN1Type.fromName(name));
- name = shuffleCase(name);
- }
- }
-
- @Test
- public void testFromTypeValue() {
- assertSame(expected, ASN1Type.fromTypeValue(expected.getTypeValue()));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java
deleted file mode 100644
index df8e992..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java
+++ /dev/null
@@ -1,61 +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.util.io.der;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.StreamCorruptedException;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class DERParserTest extends BaseTestSupport {
- public DERParserTest() {
- super();
- }
-
- @Test
- public void testReadLengthConstraint() throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try {
- try (DERWriter w = new DERWriter(baos)) {
- w.writeLength(DERParser.MAX_DER_VALUE_LENGTH + 1);
- }
- } finally {
- baos.close();
- }
-
- try (DERParser parser = new DERParser(baos.toByteArray())) {
- int len = parser.readLength();
- fail("Unexpected success: len=" + len);
- } catch (StreamCorruptedException e) {
- // expected ignored
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERWriterTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERWriterTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERWriterTest.java
deleted file mode 100644
index 9abe446..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERWriterTest.java
+++ /dev/null
@@ -1,64 +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.util.io.der;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class DERWriterTest extends BaseTestSupport {
- public DERWriterTest() {
- super();
- }
-
- @Test
- public void testWriteStripLeadingZeroes() throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try {
- try (DERWriter w = new DERWriter(baos)) {
- w.writeBigInteger(BigInteger.valueOf(-1));
- w.writeBigInteger(BigInteger.valueOf(129));
- w.writeBigInteger(new byte[] {0, 0}, 0, 2);
- w.writeBigInteger(new byte[] {0, 1}, 0, 2);
- }
- } finally {
- baos.close();
- }
-
- try (DERParser parser = new DERParser(baos.toByteArray())) {
- assertEquals(BigInteger.valueOf(255), parser.readBigInteger());
- assertEquals(BigInteger.valueOf(129), parser.readBigInteger());
- assertEquals(BigInteger.valueOf(0), parser.readBigInteger());
- assertEquals(BigInteger.valueOf(1), parser.readBigInteger());
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/net/SshdSocketIpv6AddressTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/net/SshdSocketIpv6AddressTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/net/SshdSocketIpv6AddressTest.java
deleted file mode 100644
index cc69df5..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/net/SshdSocketIpv6AddressTest.java
+++ /dev/null
@@ -1,88 +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.util.net;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class SshdSocketIpv6AddressTest extends BaseTestSupport {
- public static final List<String> VALID_ADDRESSES =
- Collections.unmodifiableList(
- Arrays.asList(
- "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:db8:85a3:0:0:8a2e:370:7334",
- "2001:db8:85a3::8a2e:370:7334",
- "2001:0db8::0001", "2001:db8::1",
- "2001:db8:0:0:0:0:2:1", "2001:db8::2:1",
- "2001:db8:0000:1:1:1:1:1", "2001:db8:0:1:1:1:1:1",
- "2001:db8:85a3:8d3:1319:8a2e:370:7348",
- "fe80::1ff:fe23:4567:890a", "fe80::1ff:fe23:4567:890a%eth2",
- "fe80::1ff:fe23:4567:890a%3", "fe80:3::1ff:fe23:4567:890a",
- "::ffff:c000:0280", "::ffff:192.0.2.128"));
-
- private final String address;
- private final boolean matches;
-
- public SshdSocketIpv6AddressTest(String address, boolean matches) {
- this.address = address;
- this.matches = matches;
- }
-
- @Parameters(name = "{0}")
- public static List<Object[]> parameters() {
- return Stream
- .concat(SshdSocketAddress.WELL_KNOWN_IPV6_ADDRESSES.stream(), VALID_ADDRESSES.stream())
- .map(address -> new Object[] {address, Boolean.TRUE})
- .collect(Collectors.toList());
- }
-
- @Test
- public void testIPv6AddressValidity() {
- assertEquals(address, matches, SshdSocketAddress.isIPv6Address(address));
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName()
- + "[address=" + address
- + " , matches=" + matches
- + "]";
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java
deleted file mode 100644
index c0c89eb..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarCipherNameTest.java
+++ /dev/null
@@ -1,75 +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.util.security;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.crypto.Cipher;
-
-import org.apache.sshd.common.cipher.BuiltinCiphers;
-import org.apache.sshd.common.cipher.CipherInformation;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class SecurityProviderRegistrarCipherNameTest extends BaseTestSupport {
- private final CipherInformation cipherInfo;
-
- public SecurityProviderRegistrarCipherNameTest(CipherInformation cipherInfo) {
- this.cipherInfo = cipherInfo;
- }
-
- @Parameters(name = "{0}")
- public static List<Object[]> parameters() {
- List<Object[]> params = new ArrayList<>();
- for (CipherInformation cipherInfo : BuiltinCiphers.VALUES) {
- String algorithm = cipherInfo.getAlgorithm();
- String xform = cipherInfo.getTransformation();
- if (!xform.startsWith(algorithm)) {
- continue;
- }
-
- params.add(new Object[]{cipherInfo});
- }
- return params;
- }
-
- @Test
- public void testGetEffectiveSecurityEntityName() {
- String expected = cipherInfo.getAlgorithm();
- String actual = SecurityProviderRegistrar.getEffectiveSecurityEntityName(Cipher.class, cipherInfo.getTransformation());
- assertEquals("Mismatched pure cipher name", expected, actual);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java b/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java
deleted file mode 100644
index 57999de..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/security/SecurityProviderRegistrarTestSupport.java
+++ /dev/null
@@ -1,62 +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.util.security;
-
-import java.security.Provider;
-import java.util.Arrays;
-import java.util.Collection;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.experimental.categories.Category;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@Category({ NoIoTestCase.class })
-public abstract class SecurityProviderRegistrarTestSupport extends BaseTestSupport {
- protected SecurityProviderRegistrarTestSupport() {
- super();
- }
-
- public static Provider testGetSecurityProviderCaching(String prefix, SecurityProviderRegistrar registrar) {
- return testGetSecurityProviderCaching(prefix, registrar, registrar.getSecurityProvider());
- }
-
- public static <P extends Provider> P testGetSecurityProviderCaching(String prefix, SecurityProviderRegistrar registrar, P expected) {
- for (int index = 1; index <= Byte.SIZE; index++) {
- Provider actual = registrar.getSecurityProvider();
- assertSame(prefix + ": Mismatched provider instance at invocation #" + index, expected, actual);
- }
-
- return expected;
- }
-
- public static void assertSecurityEntitySupportState(
- String prefix, SecurityProviderRegistrar registrar, boolean expected, String name, Class<?>... entities) {
- assertSecurityEntitySupportState(prefix, registrar, expected, name, Arrays.asList(entities));
- }
-
- public static void assertSecurityEntitySupportState(
- String prefix, SecurityProviderRegistrar registrar, boolean expected, String name, Collection<Class<?>> entities) {
- for (Class<?> entity : entities) {
- assertEquals(prefix + "[" + entity.getSimpleName() + "]", expected, registrar.isSecurityEntitySupported(entity, name));
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java
deleted file mode 100644
index d30fe92..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java
+++ /dev/null
@@ -1,126 +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.util.security.eddsa;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.Signature;
-
-import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.BeforeClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-import net.i2p.crypto.eddsa.EdDSAEngine;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class EDDSAProviderTest extends BaseTestSupport {
- private static KeyPair keyPair;
-
- public EDDSAProviderTest() {
- super();
- }
-
- @BeforeClass
- public static void checkProviderSupported() throws GeneralSecurityException {
- Assume.assumeTrue(SecurityUtils.EDDSA + " not supported", SecurityUtils.isEDDSACurveSupported());
- KeyPairGenerator g = SecurityUtils.getKeyPairGenerator(SecurityUtils.EDDSA);
- assertNotNull("No generator instance", g);
-
- keyPair = g.generateKeyPair();
- assertNotNull("No key pair generated", keyPair);
-
- PublicKey pubKey = keyPair.getPublic();
- assertNotNull("No public key", pubKey);
- assertEquals("Mismatched public key algorithm", SecurityUtils.EDDSA, pubKey.getAlgorithm());
- assertEquals("Mismatched public key type", KeyPairProvider.SSH_ED25519, KeyUtils.getKeyType(pubKey));
-
- PrivateKey prvKey = keyPair.getPrivate();
- assertNotNull("No private key", prvKey);
- assertEquals("Mismatched key-pair algorithm", pubKey.getAlgorithm(), prvKey.getAlgorithm());
- assertEquals("Mismatched private key type", KeyPairProvider.SSH_ED25519, KeyUtils.getKeyType(prvKey));
- }
-
- @Test
- public void testSignature() throws GeneralSecurityException {
- Signature s = SecurityUtils.getSignature(EdDSAEngine.SIGNATURE_ALGORITHM);
- assertNotNull("No signature instance", s);
- s.initSign(keyPair.getPrivate());
-
- byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
- s.update(data);
- byte[] signed = s.sign();
-
- s = SecurityUtils.getSignature(EdDSAEngine.SIGNATURE_ALGORITHM);
- s.initVerify(keyPair.getPublic());
- s.update(data);
- assertTrue("Failed to verify", s.verify(signed));
- }
-
- @Test
- public void testPublicKeyEntryDecoder() throws IOException, GeneralSecurityException {
- String comment = getCurrentTestName() + "@" + getClass().getSimpleName();
- String expected = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGPKSUTyz1HwHReFVvD5obVsALAgJRNarH4TRpNePnAS " + comment;
- AuthorizedKeyEntry keyEntry = AuthorizedKeyEntry.parseAuthorizedKeyEntry(expected);
- assertNotNull("No extracted key entry", keyEntry);
-
- assertEquals("Mismatched key type", KeyPairProvider.SSH_ED25519, keyEntry.getKeyType());
- assertEquals("Mismatched comment", comment, keyEntry.getComment());
-
- StringBuilder sb = new StringBuilder(expected.length());
- PublicKey pubKey = keyEntry.appendPublicKey(sb, null);
- assertEquals("Mismatched encoded result", expected, sb.toString());
-
- testPublicKeyRecovery(pubKey);
- }
-
- @Test
- public void testGeneratedPublicKeyRecovery() throws IOException, GeneralSecurityException {
- testPublicKeyRecovery(keyPair.getPublic());
- }
-
- private void testPublicKeyRecovery(PublicKey pubKey) throws IOException, GeneralSecurityException {
- assertNotNull("No public key generated", pubKey);
- assertEquals("Mismatched public key algorithm", SecurityUtils.EDDSA, pubKey.getAlgorithm());
-
- Buffer buf = SecurityUtils.putRawEDDSAPublicKey(new ByteArrayBuffer(), pubKey);
- PublicKey actual = buf.getRawPublicKey();
- assertEquals("Mismatched key algorithm", pubKey.getAlgorithm(), actual.getAlgorithm());
- assertEquals("Mismatched recovered key", pubKey, actual);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/Ed25519VectorsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/Ed25519VectorsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/Ed25519VectorsTest.java
deleted file mode 100644
index 23e5517..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/Ed25519VectorsTest.java
+++ /dev/null
@@ -1,238 +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.util.security.eddsa;
-
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.sshd.common.signature.Signature;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.BeforeClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-import net.i2p.crypto.eddsa.EdDSAPrivateKey;
-import net.i2p.crypto.eddsa.EdDSAPublicKey;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-02#section-6">
- * EdDSA and Ed25519 draft-josefsson-eddsa-ed25519-02 - section 6 - Test Vectors for Ed25519</A>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class Ed25519VectorsTest extends BaseTestSupport {
- private final byte[] prvBytes;
- private final PrivateKey privateKey;
- private final byte[] pubBytes;
- private final PublicKey publicKey;
- private final byte[] msgBytes;
- private final byte[] expSignature;
-
- public Ed25519VectorsTest(String name, String prvKey, String pubKey, String msg, String signature)
- throws GeneralSecurityException {
- prvBytes = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, prvKey);
- privateKey = EdDSASecurityProviderUtils.generateEDDSAPrivateKey(prvBytes.clone());
- pubBytes = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, pubKey);
- publicKey = EdDSASecurityProviderUtils.generateEDDSAPublicKey(pubBytes.clone());
- msgBytes = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, msg);
- expSignature = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, signature);
- }
-
- @Parameters(name = "{0}")
- @SuppressWarnings("checkstyle:anoninnerlength")
- public static List<Object[]> parameters() {
- return new ArrayList<>(Arrays.asList(
- new Object[]{
- "TEST1 - empty message",
- "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
- "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
- "",
- "e5564300c360ac729086e2cc806e828a"
- + "84877f1eb8e5d974d873e06522490155"
- + "5fb8821590a33bacc61e39701cf9b46b"
- + "d25bf5f0595bbe24655141438e7a100b"
- },
- new Object[]{
- "TEST2 - one byte",
- "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
- "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
- "72",
- "92a009a9f0d4cab8720e820b5f642540"
- + "a2b27b5416503f8fb3762223ebdb69da"
- + "085ac1e43e15996e458f3613d0f11d8c"
- + "387b2eaeb4302aeeb00d291612bb0c00"
- },
- new Object[]{
- "TEST3 - 2 bytes",
- "c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7",
- "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025",
- "af82",
- "6291d657deec24024827e69c3abe01a3"
- + "0ce548a284743a445e3680d7db5ac3ac"
- + "18ff9b538d16f290ae67f760984dc659"
- + "4a7c15e9716ed28dc027beceea1ec40a"
- },
- new Object[]{
- "TEST1024 - large message",
- "f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5",
- "278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e",
- "08b8b2b733424243760fe426a4b54908"
- + "632110a66c2f6591eabd3345e3e4eb98"
- + "fa6e264bf09efe12ee50f8f54e9f77b1"
- + "e355f6c50544e23fb1433ddf73be84d8"
- + "79de7c0046dc4996d9e773f4bc9efe57"
- + "38829adb26c81b37c93a1b270b20329d"
- + "658675fc6ea534e0810a4432826bf58c"
- + "941efb65d57a338bbd2e26640f89ffbc"
- + "1a858efcb8550ee3a5e1998bd177e93a"
- + "7363c344fe6b199ee5d02e82d522c4fe"
- + "ba15452f80288a821a579116ec6dad2b"
- + "3b310da903401aa62100ab5d1a36553e"
- + "06203b33890cc9b832f79ef80560ccb9"
- + "a39ce767967ed628c6ad573cb116dbef"
- + "efd75499da96bd68a8a97b928a8bbc10"
- + "3b6621fcde2beca1231d206be6cd9ec7"
- + "aff6f6c94fcd7204ed3455c68c83f4a4"
- + "1da4af2b74ef5c53f1d8ac70bdcb7ed1"
- + "85ce81bd84359d44254d95629e9855a9"
- + "4a7c1958d1f8ada5d0532ed8a5aa3fb2"
- + "d17ba70eb6248e594e1a2297acbbb39d"
- + "502f1a8c6eb6f1ce22b3de1a1f40cc24"
- + "554119a831a9aad6079cad88425de6bd"
- + "e1a9187ebb6092cf67bf2b13fd65f270"
- + "88d78b7e883c8759d2c4f5c65adb7553"
- + "878ad575f9fad878e80a0c9ba63bcbcc"
- + "2732e69485bbc9c90bfbd62481d9089b"
- + "eccf80cfe2df16a2cf65bd92dd597b07"
- + "07e0917af48bbb75fed413d238f5555a"
- + "7a569d80c3414a8d0859dc65a46128ba"
- + "b27af87a71314f318c782b23ebfe808b"
- + "82b0ce26401d2e22f04d83d1255dc51a"
- + "ddd3b75a2b1ae0784504df543af8969b"
- + "e3ea7082ff7fc9888c144da2af58429e"
- + "c96031dbcad3dad9af0dcbaaaf268cb8"
- + "fcffead94f3c7ca495e056a9b47acdb7"
- + "51fb73e666c6c655ade8297297d07ad1"
- + "ba5e43f1bca32301651339e22904cc8c"
- + "42f58c30c04aafdb038dda0847dd988d"
- + "cda6f3bfd15c4b4c4525004aa06eeff8"
- + "ca61783aacec57fb3d1f92b0fe2fd1a8"
- + "5f6724517b65e614ad6808d6f6ee34df"
- + "f7310fdc82aebfd904b01e1dc54b2927"
- + "094b2db68d6f903b68401adebf5a7e08"
- + "d78ff4ef5d63653a65040cf9bfd4aca7"
- + "984a74d37145986780fc0b16ac451649"
- + "de6188a7dbdf191f64b5fc5e2ab47b57"
- + "f7f7276cd419c17a3ca8e1b939ae49e4"
- + "88acba6b965610b5480109c8b17b80e1"
- + "b7b750dfc7598d5d5011fd2dcc5600a3"
- + "2ef5b52a1ecc820e308aa342721aac09"
- + "43bf6686b64b2579376504ccc493d97e"
- + "6aed3fb0f9cd71a43dd497f01f17c0e2"
- + "cb3797aa2a2f256656168e6c496afc5f"
- + "b93246f6b1116398a346f1a641f3b041"
- + "e989f7914f90cc2c7fff357876e506b5"
- + "0d334ba77c225bc307ba537152f3f161"
- + "0e4eafe595f6d9d90d11faa933a15ef1"
- + "369546868a7f3a45a96768d40fd9d034"
- + "12c091c6315cf4fde7cb68606937380d"
- + "b2eaaa707b4c4185c32eddcdd306705e"
- + "4dc1ffc872eeee475a64dfac86aba41c"
- + "0618983f8741c5ef68d3a101e8a3b8ca"
- + "c60c905c15fc910840b94c00a0b9d0",
- "0aab4c900501b3e24d7cdf4663326a3a"
- + "87df5e4843b2cbdb67cbf6e460fec350"
- + "aa5371b1508f9f4528ecea23c436d94b"
- + "5e8fcd4f681e30a6ac00a9704a188a03"
- }));
- }
-
- @BeforeClass
- public static void checkEDDSASupported() {
- Assume.assumeTrue("EDDSA N/A", SecurityUtils.isEDDSACurveSupported());
- }
-
- @Test
- public void testPublicKeyBytes() {
- byte[] publicSeed = Ed25519PublicKeyDecoder.getSeedValue((EdDSAPublicKey) publicKey);
- assertArrayEquals("Mismatched public seed value", pubBytes, publicSeed);
- }
-
- @Test
- public void testPrivateKeyBytes() {
- assertArrayEquals("Mismatched private seed value", prvBytes, ((EdDSAPrivateKey) privateKey).getSeed());
- }
-
- @Test
- public void testSignature() throws Exception {
- Signature signer = EdDSASecurityProviderUtils.getEDDSASignature();
- signer.initSigner(privateKey);
- signer.update(msgBytes.clone());
-
- byte[] actSignature = signer.sign();
- assertArrayEquals("Mismatched signature", expSignature, actSignature);
-
- Signature verifier = EdDSASecurityProviderUtils.getEDDSASignature();
- verifier.initVerifier(publicKey);
- verifier.update(msgBytes.clone());
- assertTrue("Verification failed", verifier.verify(expSignature));
- }
-
- @Test
- public void testPartialBufferSignature() throws Exception {
- byte[] extraData = getCurrentTestName().getBytes(StandardCharsets.UTF_8);
- byte[] dataBuf = new byte[msgBytes.length + extraData.length];
- int offset = extraData.length / 2;
- System.arraycopy(extraData, 0, dataBuf, 0, offset);
- System.arraycopy(msgBytes, 0, dataBuf, offset, msgBytes.length);
- System.arraycopy(extraData, offset, dataBuf, offset + msgBytes.length, extraData.length - offset);
-
- Signature signer = EdDSASecurityProviderUtils.getEDDSASignature();
- signer.initSigner(privateKey);
- signer.update(dataBuf.clone(), offset, msgBytes.length);
-
- byte[] actSignature = signer.sign();
- assertArrayEquals("Mismatched signature", expSignature, actSignature);
-
- Signature verifier = EdDSASecurityProviderUtils.getEDDSASignature();
- verifier.initVerifier(publicKey);
- verifier.update(dataBuf.clone(), offset, msgBytes.length);
- assertTrue("Verification failed", verifier.verify(expSignature));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java
deleted file mode 100644
index c2ce550..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java
+++ /dev/null
@@ -1,85 +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.util.security.eddsa;
-
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.security.Provider;
-import java.security.Signature;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-
-import org.apache.sshd.common.util.security.SecurityProviderRegistrar;
-import org.apache.sshd.common.util.security.SecurityProviderRegistrarTestSupport;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.junit.Assume;
-import org.junit.BeforeClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-import net.i2p.crypto.eddsa.EdDSASecurityProvider;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class EdDSASecurityProviderRegistrarTest extends SecurityProviderRegistrarTestSupport {
- private static SecurityProviderRegistrar registrarInstance;
-
- public EdDSASecurityProviderRegistrarTest() {
- super();
- }
-
- @BeforeClass
- public static void checkEDDSASupported() {
- Assume.assumeTrue(SecurityUtils.isEDDSACurveSupported());
- registrarInstance = new EdDSASecurityProviderRegistrar();
- }
-
- @Test
- public void testSupportedSecurityEntities() {
- assertSecurityEntitySupportState(getCurrentTestName(), registrarInstance, true, registrarInstance.getName(),
- KeyPairGenerator.class, KeyFactory.class);
- assertSecurityEntitySupportState(getCurrentTestName(), registrarInstance, true,
- SecurityUtils.CURVE_ED25519_SHA512, Signature.class);
-
- Collection<Class<?>> supported = new HashSet<>(Arrays.asList(KeyPairGenerator.class, KeyFactory.class, Signature.class));
- for (Class<?> entity : SecurityProviderRegistrar.SECURITY_ENTITIES) {
- if (supported.contains(entity)) {
- continue;
- }
- assertFalse("Unexpected support for " + entity.getSimpleName(), registrarInstance.isSecurityEntitySupported(entity, registrarInstance.getName()));
- }
- }
-
- @Test
- public void testGetSecurityProvider() {
- Provider expected = registrarInstance.getSecurityProvider();
- assertNotNull("No provider created", expected);
- assertEquals("Mismatched provider name", registrarInstance.getName(), expected.getName());
- assertObjectInstanceOf("Mismatched provider type", EdDSASecurityProvider.class, expected);
- }
-
- @Test
- public void testGetSecurityProviderCaching() {
- testGetSecurityProviderCaching(getCurrentTestName(), registrarInstance);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java
index 01c96b4..50d83ed 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java
@@ -44,7 +44,7 @@ import org.apache.sshd.server.auth.keyboard.KeyboardInteractiveAuthenticator;
import org.apache.sshd.server.auth.password.PasswordAuthenticator;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
@@ -68,11 +68,11 @@ public class ServerSessionListenerTest extends BaseTestSupport {
@BeforeClass
public static void setupClientAndServer() throws Exception {
- sshd = Utils.setupTestServer(ServerSessionListenerTest.class);
+ sshd = CoreTestSupportUtils.setupTestServer(ServerSessionListenerTest.class);
sshd.start();
port = sshd.getPort();
- client = Utils.setupTestClient(ServerSessionListenerTest.class);
+ client = CoreTestSupportUtils.setupTestClient(ServerSessionListenerTest.class);
client.start();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerPhaseTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerPhaseTest.java b/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerPhaseTest.java
index 35f032b..bcb5311 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerPhaseTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerPhaseTest.java
@@ -30,8 +30,8 @@ import org.apache.sshd.common.PropertyResolverUtils;
import org.apache.sshd.server.ServerAuthenticationManager;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.Utils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
@@ -66,11 +66,11 @@ public class WelcomeBannerPhaseTest extends BaseTestSupport {
@BeforeClass
public static void setupClientAndServer() throws Exception {
- sshd = Utils.setupTestServer(WelcomeBannerPhaseTest.class);
+ sshd = CoreTestSupportUtils.setupTestServer(WelcomeBannerPhaseTest.class);
sshd.start();
port = sshd.getPort();
- client = Utils.setupTestClient(WelcomeBannerPhaseTest.class);
+ client = CoreTestSupportUtils.setupTestClient(WelcomeBannerPhaseTest.class);
client.start();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java
index 04bf7d6..e417aa9 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java
@@ -39,7 +39,7 @@ import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.server.ServerAuthenticationManager;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
@@ -61,11 +61,11 @@ public class WelcomeBannerTest extends BaseTestSupport {
@BeforeClass
public static void setupClientAndServer() throws Exception {
- sshd = Utils.setupTestServer(WelcomeBannerTest.class);
+ sshd = CoreTestSupportUtils.setupTestServer(WelcomeBannerTest.class);
sshd.start();
port = sshd.getPort();
- client = Utils.setupTestClient(WelcomeBannerTest.class);
+ client = CoreTestSupportUtils.setupTestClient(WelcomeBannerTest.class);
client.start();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
deleted file mode 100644
index 7a2d326..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
+++ /dev/null
@@ -1,83 +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.server.keyprovider;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class AbstractGeneratorHostKeyProviderTest extends BaseTestSupport {
- public AbstractGeneratorHostKeyProviderTest() {
- super();
- }
-
- @SuppressWarnings("synthetic-access")
- @Test
- public void testOverwriteKey() throws Exception {
- Path tempDir = assertHierarchyTargetFolderExists(getTempTargetFolder());
- Path keyPairFile = tempDir.resolve(getCurrentTestName() + ".key");
- Files.deleteIfExists(keyPairFile);
-
- TestProvider provider = new TestProvider(keyPairFile);
- provider.loadKeys();
- assertEquals("Mismatched generate write count", 1, provider.getWriteCount());
-
- provider = new TestProvider(keyPairFile);
- provider.setOverwriteAllowed(false);
- provider.loadKeys();
- assertEquals("Mismatched load write count", 0, provider.getWriteCount());
- }
-
- private static final class TestProvider extends AbstractGeneratorHostKeyProvider {
- private final AtomicInteger writes = new AtomicInteger(0);
-
- private TestProvider(Path file) {
- setKeySize(512);
- setPath(file);
- }
-
- @Override
- protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException {
- return null;
- }
-
- @Override
- protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
- writes.incrementAndGet();
- }
-
- public int getWriteCount() {
- return writes.get();
- }
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
deleted file mode 100644
index 620033c..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
+++ /dev/null
@@ -1,141 +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.server.keyprovider;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.ECGenParameterSpec;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class PEMGeneratorHostKeyProviderTest extends BaseTestSupport {
- public PEMGeneratorHostKeyProviderTest() {
- super();
- }
-
- @Test
- public void testDSA() throws IOException {
- Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
- testPEMGeneratorHostKeyProvider(KeyUtils.DSS_ALGORITHM, KeyPairProvider.SSH_DSS, 512, null);
- }
-
- @Test
- public void testRSA() throws IOException {
- Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
- testPEMGeneratorHostKeyProvider(KeyUtils.RSA_ALGORITHM, KeyPairProvider.SSH_RSA, 512, null);
- }
-
- @Test
- public void testECnistp256() throws IOException {
- Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
- Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
- Assume.assumeTrue(ECCurves.nistp256 + " N/A", ECCurves.nistp256.isSupported());
- testPEMGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP256, -1, new ECGenParameterSpec("prime256v1"));
- }
-
- @Test
- public void testECnistp384() throws IOException {
- Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
- Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
- Assume.assumeTrue(ECCurves.nistp384 + " N/A", ECCurves.nistp384.isSupported());
- testPEMGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP384, -1, new ECGenParameterSpec("P-384"));
- }
-
- @Test
- public void testECnistp521() throws IOException {
- Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
- Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
- Assume.assumeTrue(ECCurves.nistp521 + " N/A", ECCurves.nistp521.isSupported());
- testPEMGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP521, -1, new ECGenParameterSpec("P-521"));
- }
-
- private Path testPEMGeneratorHostKeyProvider(String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) throws IOException {
- Path path = initKeyFileLocation(algorithm);
- KeyPair kpWrite = invokePEMGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
- assertTrue("Key file not generated: " + path, Files.exists(path, IoUtils.EMPTY_LINK_OPTIONS));
-
- KeyPair kpRead = invokePEMGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
- PublicKey pubWrite = kpWrite.getPublic();
- PublicKey pubRead = kpRead.getPublic();
- if (pubWrite instanceof ECPublicKey) {
- // The algorithm is reported as ECDSA instead of EC
- assertECPublicKeyEquals("Mismatched EC public key", ECPublicKey.class.cast(pubWrite), ECPublicKey.class.cast(pubRead));
- } else {
- assertKeyEquals("Mismatched public keys", pubWrite, pubRead);
- }
- return path;
- }
-
- private static KeyPair invokePEMGeneratorHostKeyProvider(Path path, String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) {
- AbstractGeneratorHostKeyProvider provider = SecurityUtils.createGeneratorHostKeyProvider(path.toAbsolutePath().normalize());
- provider.setAlgorithm(algorithm);
- provider.setOverwriteAllowed(true);
- if (keySize > 0) {
- provider.setKeySize(keySize);
- }
- if (keySpec != null) {
- provider.setKeySpec(keySpec);
- }
-
- return validateKeyPairProvider(provider, keyType);
- }
-
- private static KeyPair validateKeyPairProvider(KeyPairProvider provider, String keyType) {
- Iterable<String> types = provider.getKeyTypes();
- KeyPair kp = null;
- for (String type : types) {
- if (keyType.equals(type)) {
- kp = provider.loadKey(keyType);
- assertNotNull("Failed to load key for " + keyType, kp);
- break;
- }
- }
-
- assertNotNull("Expected key type not found: " + keyType, kp);
- return kp;
- }
-
- private Path initKeyFileLocation(String algorithm) throws IOException {
- Path path = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
- path = path.resolve(algorithm + "-PEM.key");
- Files.deleteIfExists(path);
- return path;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java b/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
deleted file mode 100644
index 1ac3ef9..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
+++ /dev/null
@@ -1,133 +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.server.keyprovider;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.KeyPair;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.ECGenParameterSpec;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class SimpleGeneratorHostKeyProviderTest extends BaseTestSupport {
- public SimpleGeneratorHostKeyProviderTest() {
- super();
- }
-
- @Test
- public void testDSA() throws IOException {
- testSimpleGeneratorHostKeyProvider(KeyUtils.DSS_ALGORITHM, KeyPairProvider.SSH_DSS, 512, null);
- }
-
- @Test
- public void testRSA() throws IOException {
- testSimpleGeneratorHostKeyProvider(KeyUtils.RSA_ALGORITHM, KeyPairProvider.SSH_RSA, 512, null);
- }
-
- @Test
- public void testECnistp256() throws IOException {
- Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
- Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
- Assume.assumeTrue(ECCurves.nistp256 + " N/A", ECCurves.nistp256.isSupported());
- testSimpleGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP256, -1, new ECGenParameterSpec("prime256v1"));
- }
-
- @Test
- public void testECnistp384() throws IOException {
- Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
- Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
- Assume.assumeTrue(ECCurves.nistp384 + " N/A", ECCurves.nistp384.isSupported());
- testSimpleGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP384, -1, new ECGenParameterSpec("P-384"));
- }
-
- @Test
- public void testECnistp521() throws IOException {
- Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
- Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
- Assume.assumeTrue(ECCurves.nistp521 + " N/A", ECCurves.nistp521.isSupported());
- testSimpleGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP521, -1, new ECGenParameterSpec("P-521"));
- }
-
- private Path testSimpleGeneratorHostKeyProvider(String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) throws IOException {
- Path path = initKeyFileLocation(algorithm);
- KeyPair kpWrite = invokeSimpleGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
- assertTrue("Key file not generated: " + path, Files.exists(path, IoUtils.EMPTY_LINK_OPTIONS));
-
- KeyPair kpRead = invokeSimpleGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
- assertKeyPairEquals("Mismatched write/read key pairs", kpWrite, kpRead);
- return path;
- }
-
- private static KeyPair invokeSimpleGeneratorHostKeyProvider(Path path, String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) {
- SimpleGeneratorHostKeyProvider provider = new SimpleGeneratorHostKeyProvider();
- provider.setAlgorithm(algorithm);
- provider.setOverwriteAllowed(true);
- provider.setPath(path);
- if (keySize > 0) {
- provider.setKeySize(keySize);
- }
- if (keySpec != null) {
- provider.setKeySpec(keySpec);
- }
-
- return validateKeyPairProvider(provider, keyType);
- }
-
- private static KeyPair validateKeyPairProvider(KeyPairProvider provider, String keyType) {
- Iterable<String> types = provider.getKeyTypes();
- KeyPair kp = null;
- for (String type : types) {
- if (keyType.equals(type)) {
- kp = provider.loadKey(keyType);
- assertNotNull("Failed to load key for " + keyType, kp);
- break;
- }
- }
-
- assertNotNull("Expected key type not found: " + keyType, kp);
- return kp;
- }
-
- private Path initKeyFileLocation(String algorithm) throws IOException {
- Path path = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
- path = path.resolve(algorithm + "-simple.key");
- Files.deleteIfExists(path);
- return path;
- }
-}
[23/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
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,umac-64@openssh.com,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:dev@mina.apache.org">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:dev@mina.apache.org">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:dev@mina.apache.org">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:dev@mina.apache.org">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:dev@mina.apache.org">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:dev@mina.apache.org">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:dev@mina.apache.org">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:dev@mina.apache.org">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:dev@mina.apache.org">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:dev@mina.apache.org">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);
- }
-}
[12/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java
deleted file mode 100644
index b665166..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/AbstractSecurityProviderRegistrar.java
+++ /dev/null
@@ -1,129 +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.util.security;
-
-import java.security.Provider;
-import java.security.Security;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractSecurityProviderRegistrar
- extends AbstractLoggingBean
- implements SecurityProviderRegistrar {
- protected final Map<String, Object> props = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- protected final Map<Class<?>, Map<String, Boolean>> supportedEntities = new HashMap<>();
- protected final AtomicReference<Provider> providerHolder = new AtomicReference<>(null);
-
- private final String name;
-
- protected AbstractSecurityProviderRegistrar(String name) {
- this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name provided");
- }
-
- @Override
- public final String getName() {
- return name;
- }
-
- @Override
- public Map<String, Object> getProperties() {
- return props;
- }
-
- @Override
- public boolean isSecurityEntitySupported(Class<?> entityType, String name) {
- Map<String, Boolean> supportMap;
- synchronized (supportedEntities) {
- supportMap = supportedEntities.computeIfAbsent(
- entityType, k -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
- }
-
- Boolean supportFlag;
- synchronized (supportMap) {
- supportFlag = supportMap.computeIfAbsent(
- name, k -> SecurityProviderRegistrar.super.isSecurityEntitySupported(entityType, name));
- }
-
- return supportFlag;
- }
-
- /**
- * Attempts to see if a provider with this name already registered. If not,
- * then uses reflection API in order to load and instantiate the specified
- * <tt>providerClassName</tt>
- *
- * @param providerClassName The fully-qualified class name to instantiate
- * if a provider not already registered
- * @return The resolved {@link Provider} instance - <B>Note:</B> the result
- * is <U>cached</U> - i.e., successful resolution result will not cause
- * the code to re-resolve the provider
- * @throws ReflectiveOperationException If failed to instantiate the provider
- * @throws UnsupportedOperationException If registrar not supported
- * @see #isSupported()
- * @see Security#getProvider(String)
- * @see #createProviderInstance(String)
- */
- protected Provider getOrCreateProvider(String providerClassName) throws ReflectiveOperationException {
- if (!isSupported()) {
- throw new UnsupportedOperationException("Provider not supported");
- }
-
- Provider provider;
- boolean created = false;
- synchronized (providerHolder) {
- provider = providerHolder.get();
- if (provider != null) {
- return provider;
- }
-
- provider = Security.getProvider(getName());
- if (provider == null) {
- provider = createProviderInstance(providerClassName);
- created = true;
- }
- providerHolder.set(provider);
- }
-
- if (created) {
- log.info("getOrCreateProvider({}) created instance of {}", getName(), providerClassName);
- } else {
- log.info("getOrCreateProvider({}) resolved instance of {}", getName(), provider.getClass().getName());
- }
-
- return provider;
- }
-
- protected Provider createProviderInstance(String providerClassName) throws ReflectiveOperationException {
- return SecurityProviderChoice.createProviderInstance(getClass(), providerClassName);
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + "[" + getName() + "]";
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
deleted file mode 100644
index 94b9454..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityEntityFactory.java
+++ /dev/null
@@ -1,194 +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.util.security;
-
-import java.lang.reflect.Method;
-import java.security.GeneralSecurityException;
-import java.security.Provider;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @param <T> Type of security entity being generated by this factory
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface SecurityEntityFactory<T> {
- Class<T> getEntityType();
-
- T getInstance(String algorithm) throws GeneralSecurityException;
-
- /**
- * Uses reflection in order to wrap the {@code getInstance} method(s)
- * as a security entity factory.
- *
- * @param <F> Type of entity being generated by the factor
- * @param entityType The entity type class
- * @param registrar The {@code SecurityProviderRegistrar} to use - if
- * {@code null} then default provider is used (if specified).
- * @param defaultProvider Default provider choice to use if no registrar
- * provided. If {@code null}/empty then JCE default is used
- * @return The {@link SecurityEntityFactory} for the entity
- * @throws ReflectiveOperationException If failed to create the factory
- * @see #toDefaultFactory(Class)
- * @see #toNamedProviderFactory(Class, String)
- * @see #toProviderInstanceFactory(Class, Provider)
- * @see SecurityProviderChoice#isNamedProviderUsed()
- * @see SecurityProviderChoice#getSecurityProvider()
- */
- static <F> SecurityEntityFactory<F> toFactory(
- Class<F> entityType, SecurityProviderChoice registrar, SecurityProviderChoice defaultProvider)
- throws ReflectiveOperationException {
- if (registrar == null) {
- if ((defaultProvider == null) || (defaultProvider == SecurityProviderChoice.EMPTY)) {
- return toDefaultFactory(entityType);
- } else if (defaultProvider.isNamedProviderUsed()) {
- return toNamedProviderFactory(entityType, defaultProvider.getName());
- } else {
- return toProviderInstanceFactory(entityType, defaultProvider.getSecurityProvider());
- }
- } else if (registrar.isNamedProviderUsed()) {
- return toNamedProviderFactory(entityType, registrar.getName());
- } else {
- return toProviderInstanceFactory(entityType, registrar.getSecurityProvider());
- }
- }
-
- static <F> SecurityEntityFactory<F> toDefaultFactory(Class<F> entityType)
- throws ReflectiveOperationException {
- Method m = entityType.getDeclaredMethod("getInstance", String.class);
- return new SecurityEntityFactory<F>() {
- private final String s = SecurityEntityFactory.class.getSimpleName()
- + "[" + entityType.getSimpleName() + "]"
- + "[default]";
-
- @Override
- public Class<F> getEntityType() {
- return entityType;
- }
-
- @Override
- public F getInstance(String algorithm) throws GeneralSecurityException {
- try {
- Object value = m.invoke(null, algorithm);
- return entityType.cast(value);
- } catch (ReflectiveOperationException t) {
- Throwable e = GenericUtils.peelException(t);
- if (e instanceof GeneralSecurityException) {
- throw (GeneralSecurityException) e;
- } else if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- } else if (e instanceof Error) {
- throw (Error) e;
- } else {
- throw new GeneralSecurityException(e);
- }
- }
- }
-
- @Override
- public String toString() {
- return s;
- }
- };
- }
-
- static <F> SecurityEntityFactory<F> toNamedProviderFactory(Class<F> entityType, String name)
- throws ReflectiveOperationException {
- ValidateUtils.checkNotNullAndNotEmpty(name, "No provider name specified");
- Method m = entityType.getDeclaredMethod("getInstance", String.class, String.class);
- return new SecurityEntityFactory<F>() {
- private final String s = SecurityEntityFactory.class.getSimpleName()
- + "[" + entityType.getSimpleName() + "]"
- + "[" + name + "]";
-
- @Override
- public Class<F> getEntityType() {
- return entityType;
- }
-
- @Override
- public F getInstance(String algorithm) throws GeneralSecurityException {
- try {
- Object value = m.invoke(null, algorithm, name);
- return entityType.cast(value);
- } catch (ReflectiveOperationException t) {
- Throwable e = GenericUtils.peelException(t);
- if (e instanceof GeneralSecurityException) {
- throw (GeneralSecurityException) e;
- } else if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- } else if (e instanceof Error) {
- throw (Error) e;
- } else {
- throw new GeneralSecurityException(e);
- }
- }
- }
-
- @Override
- public String toString() {
- return s;
- }
- };
- }
-
- static <F> SecurityEntityFactory<F> toProviderInstanceFactory(Class<F> entityType, Provider provider)
- throws ReflectiveOperationException {
- Objects.requireNonNull(provider, "No provider instance");
- Method m = entityType.getDeclaredMethod("getInstance", String.class, Provider.class);
- return new SecurityEntityFactory<F>() {
- private final String s = SecurityEntityFactory.class.getSimpleName()
- + "[" + entityType.getSimpleName() + "]"
- + "[" + Provider.class.getSimpleName() + "]"
- + "[" + provider.getName() + "]";
-
- @Override
- public Class<F> getEntityType() {
- return entityType;
- }
-
- @Override
- public F getInstance(String algorithm) throws GeneralSecurityException {
- try {
- Object value = m.invoke(null, algorithm, provider);
- return entityType.cast(value);
- } catch (ReflectiveOperationException t) {
- Throwable e = GenericUtils.peelException(t);
- if (e instanceof GeneralSecurityException) {
- throw (GeneralSecurityException) e;
- } else if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- } else if (e instanceof Error) {
- throw (Error) e;
- } else {
- throw new GeneralSecurityException(e);
- }
- }
- }
-
- @Override
- public String toString() {
- return s;
- }
- };
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java
deleted file mode 100644
index c12e747..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderChoice.java
+++ /dev/null
@@ -1,130 +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.util.security;
-
-import java.security.Provider;
-import java.util.Objects;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface SecurityProviderChoice extends NamedResource {
- SecurityProviderChoice EMPTY = new SecurityProviderChoice() {
- @Override
- public String getName() {
- return null;
- }
-
- @Override
- public boolean isNamedProviderUsed() {
- return false;
- }
-
- @Override
- public Provider getSecurityProvider() {
- return null;
- }
-
- @Override
- public String toString() {
- return "EMPTY";
- }
- };
-
- /**
- * @return {@code true} if to use the provider's name rather than its
- * {@link Provider} instance - default={@code true}.
- */
- default boolean isNamedProviderUsed() {
- return true;
- }
-
- /**
- * @return The security {@link Provider} to use in case {@link #isNamedProviderUsed()}
- * is {@code false}. Can be {@code null} if {@link #isNamedProviderUsed()} is {@code true},
- * but not recommended.
- */
- Provider getSecurityProvider();
-
- static SecurityProviderChoice toSecurityProviderChoice(String name) {
- ValidateUtils.checkNotNullAndNotEmpty(name, "No name provided");
- return new SecurityProviderChoice() {
- private final String s = SecurityProviderChoice.class.getSimpleName() + "[" + name + "]";
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public boolean isNamedProviderUsed() {
- return true;
- }
-
- @Override
- public Provider getSecurityProvider() {
- return null;
- }
-
- @Override
- public String toString() {
- return s;
- }
- };
- }
-
- static SecurityProviderChoice toSecurityProviderChoice(Provider provider) {
- Objects.requireNonNull(provider, "No provider instance");
- return new SecurityProviderChoice() {
- private final String s = SecurityProviderChoice.class.getSimpleName()
- + "[" + Provider.class.getSimpleName() + "]"
- + "[" + provider.getName() + "]";
-
- @Override
- public String getName() {
- return provider.getName();
- }
-
- @Override
- public boolean isNamedProviderUsed() {
- return false;
- }
-
- @Override
- public Provider getSecurityProvider() {
- return provider;
- }
-
- @Override
- public String toString() {
- return s;
- }
- };
- }
-
- static Provider createProviderInstance(Class<?> anchor, String providerClassName)
- throws ReflectiveOperationException {
- return ThreadUtils.createDefaultInstance(anchor, Provider.class, providerClassName);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java
deleted file mode 100644
index 31e428b..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityProviderRegistrar.java
+++ /dev/null
@@ -1,337 +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.util.security;
-
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.security.MessageDigest;
-import java.security.Provider;
-import java.security.Security;
-import java.security.Signature;
-import java.security.cert.CertificateFactory;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.function.Predicate;
-
-import javax.crypto.Cipher;
-import javax.crypto.KeyAgreement;
-import javax.crypto.Mac;
-
-import org.apache.sshd.common.OptionalFeature;
-import org.apache.sshd.common.PropertyResolver;
-import org.apache.sshd.common.PropertyResolverUtils;
-import org.apache.sshd.common.SyspropsMapWrapper;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.IgnoringEmptyMap;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface SecurityProviderRegistrar extends SecurityProviderChoice, OptionalFeature, PropertyResolver {
- /**
- * Base name for configuration properties related to security providers
- */
- String CONFIG_PROP_BASE = "org.apache.sshd.security.provider";
-
- /**
- * Property used to configure whether the provider is enabled regardless of
- * whether it is supported.
- *
- * @see #isEnabled()
- */
- String ENABLED_PROPERTY = "enabled";
-
- /**
- * Property used to configure whether to use the provider's name rather than its
- * {@link Provider} instance
- *
- * @see #isNamedProviderUsed()
- */
- String NAMED_PROVIDER_PROPERTY = "useNamed";
-
- String ALL_OPTIONS_VALUE = "all";
- String ALL_OPTIONS_WILDCARD = "*";
-
- String NO_OPTIONS_VALUE = "none";
-
- /**
- * All the entities that are used in calls to {@link #isSecurityEntitySupported(Class, String)}
- */
- List<Class<?>> SECURITY_ENTITIES =
- Collections.unmodifiableList(
- Arrays.asList(
- Cipher.class, KeyFactory.class, MessageDigest.class,
- KeyPairGenerator.class, KeyAgreement.class, Mac.class,
- Signature.class, CertificateFactory.class));
-
- default String getBasePropertyName() {
- return CONFIG_PROP_BASE + "." + getName();
- }
-
- default String getConfigurationPropertyName(String name) {
- return getBasePropertyName() + "." + name;
- }
-
- /**
- * @return {@code true} if the provider is enabled regardless of
- * whether it is supported - default={@code true}. <B>Note:</B>
- * checks if the provider has been <U>programmatically</U> disabled
- * via {@link SecurityUtils#setAPrioriDisabledProvider(String, boolean)}
- * @see #ENABLED_PROPERTY
- */
- default boolean isEnabled() {
- if (SecurityUtils.isAPrioriDisabledProvider(getName())) {
- return false;
- }
-
- return this.getBooleanProperty(getConfigurationPropertyName(ENABLED_PROPERTY), true);
- }
-
- @Override
- default PropertyResolver getParentPropertyResolver() {
- return SyspropsMapWrapper.SYSPROPS_RESOLVER;
- }
-
- @Override
- default Map<String, Object> getProperties() {
- return IgnoringEmptyMap.getInstance();
- }
-
- /**
- * @param transformation The requested {@link Cipher} transformation
- * @return {@code true} if this security provider supports the transformation
- * @see #isSecurityEntitySupported(Class, String)
- */
- default boolean isCipherSupported(String transformation) {
- return isSecurityEntitySupported(Cipher.class, transformation);
- }
-
- /**
- * @param algorithm The {@link KeyFactory} algorithm
- * @return {@code true} if this security provider supports the algorithm
- * @see #isSecurityEntitySupported(Class, String)
- */
- default boolean isKeyFactorySupported(String algorithm) {
- return isSecurityEntitySupported(KeyFactory.class, algorithm);
- }
-
- /**
- * @param algorithm The {@link MessageDigest} algorithm
- * @return {@code true} if this security provider supports the algorithm
- * @see #isSecurityEntitySupported(Class, String)
- */
- default boolean isMessageDigestSupported(String algorithm) {
- return isSecurityEntitySupported(MessageDigest.class, algorithm);
- }
-
- /**
- * @param algorithm The {@link KeyPairGenerator} algorithm
- * @return {@code true} if this security provider supports the algorithm
- * @see #isSecurityEntitySupported(Class, String)
- */
- default boolean isKeyPairGeneratorSupported(String algorithm) {
- return isSecurityEntitySupported(KeyPairGenerator.class, algorithm);
- }
-
- /**
- * @param algorithm The {@link KeyAgreement} algorithm
- * @return {@code true} if this security provider supports the algorithm
- * @see #isSecurityEntitySupported(Class, String)
- */
- default boolean isKeyAgreementSupported(String algorithm) {
- return isSecurityEntitySupported(KeyAgreement.class, algorithm);
- }
-
- /**
- * @param algorithm The {@link Mac} algorithm
- * @return {@code true} if this security provider supports the algorithm
- * @see #isSecurityEntitySupported(Class, String)
- */
- default boolean isMacSupported(String algorithm) {
- return isSecurityEntitySupported(Mac.class, algorithm);
- }
-
- /**
- * @param algorithm The {@link Signature} algorithm
- * @return {@code true} if this security provider supports the algorithm
- * @see #isSecurityEntitySupported(Class, String)
- */
- default boolean isSignatureSupported(String algorithm) {
- return isSecurityEntitySupported(Signature.class, algorithm);
- }
-
- /**
- * @param type The {@link CertificateFactory} type
- * @return {@code true} if this security provider supports the algorithm
- * @see #isSecurityEntitySupported(Class, String)
- */
- default boolean isCertificateFactorySupported(String type) {
- return isSecurityEntitySupported(CertificateFactory.class, type);
- }
-
- /**
- * @param entityType The requested entity type - its simple name serves to
- * build the configuration property name.
- * @return Configuration value to use if no specific configuration provided
- * - default=empty
- * @see #isSecurityEntitySupported(Class, String)
- */
- default String getDefaultSecurityEntitySupportValue(Class<?> entityType) {
- return "";
- }
-
- default boolean isSecurityEntitySupported(Class<?> entityType, String name) {
- String defaultValue = getDefaultSecurityEntitySupportValue(entityType);
- return isSecurityEntitySupported(this, entityType, name, defaultValue);
- }
-
- /**
- * @return {@code true} if to use the provider's name rather than its
- * {@link Provider} instance - default={@code true}
- * @see #NAMED_PROVIDER_PROPERTY
- * @see #getSecurityProvider()
- * @see #registerSecurityProvider(SecurityProviderRegistrar)
- */
- @Override
- default boolean isNamedProviderUsed() {
- return PropertyResolverUtils.getBooleanProperty(this,
- getConfigurationPropertyName(NAMED_PROVIDER_PROPERTY),
- SecurityProviderChoice.super.isNamedProviderUsed());
- }
-
- /**
- * @param v Value to be examined
- * @return {@code true} if the value equals (case insensitive) to
- * either {@link #ALL_OPTIONS_VALUE} or {@link #ALL_OPTIONS_WILDCARD}
- */
- static boolean isAllOptionsValue(String v) {
- return ALL_OPTIONS_VALUE.equalsIgnoreCase(v)
- || ALL_OPTIONS_WILDCARD.equalsIgnoreCase(v);
- }
-
- /**
- * Checks whether the requested entity type algorithm/name is listed
- * as supported by the registrar's configuration
- *
- * @param registrar The {@link SecurityProviderRegistrar}
- * @param entityType The requested entity type - its simple name serves to
- * build the configuration property name.
- * @param name The requested algorithm/name - <B>Note:</B> if the requested
- * entity is a {@link Cipher} then the argument is assumed to be a possible
- * "/" separated transformation and parsed as such in order to
- * retrieve the pure cipher name
- * @param defaultValue Configuration value to use if no specific configuration provided
- * @return {@code true} registrar is supported and the value is listed
- * (case <U>insensitive</U>) or * the property is one of the "all" markers
- * @see SecurityProviderRegistrar#isSupported()
- * @see #isAllOptionsValue(String)
- */
- static boolean isSecurityEntitySupported(SecurityProviderRegistrar registrar, Class<?> entityType, String name, String defaultValue) {
- return Objects.requireNonNull(registrar, "No registrar instance").isSupported()
- && isSecurityEntitySupported(registrar, registrar.getConfigurationPropertyName(entityType.getSimpleName()), entityType, name, defaultValue);
- }
-
- static boolean isSecurityEntitySupported(PropertyResolver resolver, String propName, Class<?> entityType, String name, String defaultValue) {
- if (GenericUtils.isEmpty(name)) {
- return false;
- }
-
- String propValue = resolver.getString(propName);
- if (GenericUtils.isEmpty(propValue)) {
- propValue = defaultValue;
- }
-
- if (NO_OPTIONS_VALUE.equalsIgnoreCase(propValue)) {
- return false;
- }
-
- String[] values = GenericUtils.split(propValue, ',');
- if (GenericUtils.isEmpty(values)) {
- return false;
- }
-
- if ((values.length == 1) && isAllOptionsValue(values[0])) {
- return true;
- }
-
- String effectiveName = getEffectiveSecurityEntityName(entityType, name);
- int index = Arrays.binarySearch(values, effectiveName, String.CASE_INSENSITIVE_ORDER);
- return index >= 0;
- }
-
- /**
- * Determines the "pure" security entity name - e.g., for {@link Cipher}s
- * it strips the trailing transformation specification in order to extract the
- * base cipher name - e.g., "AES/CBC/NoPadding" => "AES"
- *
- * @param entityType The security entity type - ignored if {@code null}
- * @param name The effective name - ignored if {@code null}/empty
- * @return The resolved name
- */
- static String getEffectiveSecurityEntityName(Class<?> entityType, String name) {
- if ((entityType == null) || GenericUtils.isEmpty(name) || (!Cipher.class.isAssignableFrom(entityType))) {
- return name;
- }
-
- int pos = name.indexOf('/');
- return (pos > 0) ? name.substring(0, pos) : name;
- }
-
- /**
- * Attempts to register the security provider represented by the registrar
- * if not already registered. <B>Note:</B> if {@link SecurityProviderRegistrar#isNamedProviderUsed()}
- * is {@code true} then the generated provider will be added to the system's
- * list of known providers.
- *
- * @param registrar The {@link SecurityProviderRegistrar}
- * @return {@code true} if no provider was previously registered
- * @see Security#getProvider(String)
- * @see SecurityProviderRegistrar#getSecurityProvider()
- * @see Security#addProvider(Provider)
- */
- static boolean registerSecurityProvider(SecurityProviderRegistrar registrar) {
- String name = ValidateUtils.checkNotNullAndNotEmpty(
- (registrar == null) ? null : registrar.getName(), "No name for registrar=%s", registrar);
- Provider p = Security.getProvider(name);
- if (p != null) {
- return false;
- }
-
- p = ValidateUtils.checkNotNull(
- registrar.getSecurityProvider(), "No provider created for registrar of %s", name);
- if (registrar.isNamedProviderUsed()) {
- Security.addProvider(p);
- }
-
- return true;
- }
-
- static SecurityProviderRegistrar findSecurityProviderRegistrarBySecurityEntity(
- Predicate<? super SecurityProviderRegistrar> entitySelector,
- Collection<? extends SecurityProviderRegistrar> registrars) {
- return GenericUtils.findFirstMatchingMember(
- r -> r.isEnabled() && r.isSupported() && entitySelector.test(r), registrars);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
deleted file mode 100644
index ee755e6..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
+++ /dev/null
@@ -1,759 +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.util.security;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.math.BigInteger;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.security.cert.CertificateFactory;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Predicate;
-
-import javax.crypto.Cipher;
-import javax.crypto.KeyAgreement;
-import javax.crypto.Mac;
-import javax.crypto.spec.DHParameterSpec;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
-import org.apache.sshd.common.config.keys.loader.openssh.OpenSSHKeyPairResourceParser;
-import org.apache.sshd.common.config.keys.loader.pem.PEMResourceParserUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.random.JceRandomFactory;
-import org.apache.sshd.common.random.RandomFactory;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleGeneratorHostKeyProvider;
-import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleKeyPairResourceParser;
-import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleRandomFactory;
-import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderUtils;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Specific security providers related code
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class SecurityUtils {
- /**
- * Bouncycastle JCE provider name
- */
- public static final String BOUNCY_CASTLE = "BC";
-
- /**
- * EDDSA support - should match {@code EdDSAKey.KEY_ALGORITHM}
- */
- public static final String EDDSA = "EdDSA";
-
- // A copy-paste from the original, but we don't want to drag the classes into the classpath
- // See EdDSAEngine.SIGNATURE_ALGORITHM
- public static final String CURVE_ED25519_SHA512 = "NONEwithEdDSA";
-
- /**
- * System property used to configure the value for the maximum supported Diffie-Hellman
- * Group Exchange key size. If not set, then an internal auto-discovery mechanism is employed.
- * If set to negative value then Diffie-Hellman Group Exchange is disabled. If set to a
- * negative value then Diffie-Hellman Group Exchange is disabled
- */
- public static final String MAX_DHGEX_KEY_SIZE_PROP = "org.apache.sshd.maxDHGexKeySize";
-
- /**
- * The min. key size value used for testing whether Diffie-Hellman Group Exchange
- * is supported or not. According to <A HREF="https://tools.ietf.org/html/rfc4419">RFC 4419</A>
- * section 3: "Servers and clients SHOULD support groups with a modulus length of k
- * bits, where 1024 <= k <= 8192".
- * </code>
- */
- public static final int MIN_DHGEX_KEY_SIZE = 1024;
- // Keys of size > 1024 are not support by default with JCE
- public static final int DEFAULT_DHGEX_KEY_SIZE = MIN_DHGEX_KEY_SIZE;
- public static final int PREFERRED_DHGEX_KEY_SIZE = 4096;
- public static final int MAX_DHGEX_KEY_SIZE = 8192;
-
- /**
- * Comma separated list of fully qualified {@link SecurityProviderRegistrar}s
- * to automatically register
- */
- public static final String SECURITY_PROVIDER_REGISTRARS = "org.apache.sshd.security.registrars";
- public static final List<String> DEFAULT_SECURITY_PROVIDER_REGISTRARS =
- Collections.unmodifiableList(
- Arrays.asList(
- "org.apache.sshd.common.util.security.bouncycastle.BouncyCastleSecurityProviderRegistrar",
- "org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderRegistrar"));
-
-
- /**
- * System property used to control whether to automatically register the
- * {@code Bouncyastle} JCE provider
- * @deprecated Please use "org.apache.sshd.security.provider.BC.enabled"
- */
- @Deprecated
- public static final String REGISTER_BOUNCY_CASTLE_PROP = "org.apache.sshd.registerBouncyCastle";
-
- /**
- * System property used to control whether Elliptic Curves are supported or not.
- * If not set then the support is auto-detected. <B>Note:</B> if set to {@code true}
- * it is up to the user to make sure that indeed there is a provider for them
- */
- public static final String ECC_SUPPORTED_PROP = "org.apache.sshd.eccSupport";
-
- /**
- * System property used to decide whether EDDSA curves are supported or not
- * (in addition or even in spite of {@link #isEDDSACurveSupported()}). If not
- * set or set to {@code true}, then the existence of the optional support classes
- * determines the support.
- * @deprecated Please use "org.apache.sshd.security.provider.EdDSA.enabled&qupt;
- */
- @Deprecated
- public static final String EDDSA_SUPPORTED_PROP = "org.apache.sshd.eddsaSupport";
-
- public static final String PROP_DEFAULT_SECURITY_PROVIDER = "org.apache.sshd.security.defaultProvider";
-
- private static final AtomicInteger MAX_DHG_KEY_SIZE_HOLDER = new AtomicInteger(0);
-
- /*
- * NOTE: we use a LinkedHashMap in order to preserve registration order
- * in case several providers support the same security entity
- */
- private static final Map<String, SecurityProviderRegistrar> REGISTERED_PROVIDERS = new LinkedHashMap<>();
- private static final AtomicReference<KeyPairResourceParser> KEYPAIRS_PARSER_HODLER = new AtomicReference<>();
- // If an entry already exists for the named provider, then it overrides its SecurityProviderRegistrar#isEnabled()
- private static final Set<String> APRIORI_DISABLED_PROVIDERS = new TreeSet<>();
- private static final AtomicBoolean REGISTRATION_STATE_HOLDER = new AtomicBoolean(false);
- private static final Map<Class<?>, Map<String, SecurityEntityFactory<?>>> SECURITY_ENTITY_FACTORIES = new HashMap<>();
-
- private static final AtomicReference<SecurityProviderChoice> DEFAULT_PROVIDER_HOLDER = new AtomicReference<>();
-
- private static Boolean hasEcc;
-
- private SecurityUtils() {
- throw new UnsupportedOperationException("No instance");
- }
-
- /**
- * @param name The provider's name - never {@code null}/empty
- * @return {@code true} if the provider is marked as disabled a-priori
- * @see #setAPrioriDisabledProvider(String, boolean)
- */
- public static boolean isAPrioriDisabledProvider(String name) {
- ValidateUtils.checkNotNullAndNotEmpty(name, "No provider name specified");
- synchronized (APRIORI_DISABLED_PROVIDERS) {
- return APRIORI_DISABLED_PROVIDERS.contains(name);
- }
- }
-
- /**
- * Marks a provider's registrar as "a-priori" <U>programatically</U>
- * so that when its {@link SecurityProviderRegistrar#isEnabled()} is eventually
- * consulted it will return {@code false} regardless of the configured value for
- * the specific provider registrar instance. <B>Note:</B> has no effect if the
- * provider has already been registered.
- *
- * @param name The provider's name - never {@code null}/empty
- * @param disabled {@code true} whether to disable it a-priori
- * @see #isAPrioriDisabledProvider(String)
- */
- public static void setAPrioriDisabledProvider(String name, boolean disabled) {
- ValidateUtils.checkNotNullAndNotEmpty(name, "No provider name specified");
- synchronized (APRIORI_DISABLED_PROVIDERS) {
- if (disabled) {
- APRIORI_DISABLED_PROVIDERS.add(name);
- } else {
- APRIORI_DISABLED_PROVIDERS.remove(name);
- }
- }
- }
-
- /**
- * @return A <U>copy</U> if the current a-priori disabled providers names
- */
- public static Set<String> getAPrioriDisabledProviders() {
- synchronized (APRIORI_DISABLED_PROVIDERS) {
- return new TreeSet<>(APRIORI_DISABLED_PROVIDERS);
- }
- }
-
- /**
- * @return {@code true} if Elliptic Curve Cryptography is supported
- * @see #ECC_SUPPORTED_PROP
- */
- public static boolean isECCSupported() {
- if (hasEcc == null) {
- String propValue = System.getProperty(ECC_SUPPORTED_PROP);
- if (GenericUtils.isEmpty(propValue)) {
- try {
- getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
- hasEcc = Boolean.TRUE;
- } catch (Throwable t) {
- hasEcc = Boolean.FALSE;
- }
- } else {
- Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
- logger.info("Override ECC support value: " + propValue);
- hasEcc = Boolean.valueOf(propValue);
- }
- }
-
- return hasEcc;
- }
-
- /**
- * @return {@code true} if Diffie-Hellman Group Exchange is supported
- * @see #getMaxDHGroupExchangeKeySize()
- */
- public static boolean isDHGroupExchangeSupported() {
- return getMaxDHGroupExchangeKeySize() > 0;
- }
-
- /**
- * @param keySize The expected key size
- * @return {@code true} if Oakely Diffie-Hellman Group Exchange is supported
- * for the specified key size
- * @see #getMaxDHGroupExchangeKeySize()
- */
- public static boolean isDHOakelyGroupSupported(int keySize) {
- return getMaxDHGroupExchangeKeySize() >= keySize;
- }
-
- /**
- * @return The maximum supported Diffie-Hellman Group Exchange key size,
- * or non-positive if not supported
- */
- public static int getMaxDHGroupExchangeKeySize() {
- int maxSupportedKeySize;
- synchronized (MAX_DHG_KEY_SIZE_HOLDER) {
- maxSupportedKeySize = MAX_DHG_KEY_SIZE_HOLDER.get();
- if (maxSupportedKeySize != 0) { // 1st time we are called ?
- return maxSupportedKeySize;
- }
-
- String propValue = System.getProperty(MAX_DHGEX_KEY_SIZE_PROP);
- if (GenericUtils.isEmpty(propValue)) {
- maxSupportedKeySize = -1;
- // Go down from max. to min. to ensure we stop at 1st maximum value success
- for (int testKeySize = MAX_DHGEX_KEY_SIZE; testKeySize >= MIN_DHGEX_KEY_SIZE; testKeySize -= 1024) {
- if (isDHGroupExchangeSupported(testKeySize)) {
- maxSupportedKeySize = testKeySize;
- break;
- }
- }
- } else {
- Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
- logger.info("Override max. DH group exchange key size: " + propValue);
- maxSupportedKeySize = Integer.parseInt(propValue);
- // negative is OK - means user wants to disable DH group exchange
- ValidateUtils.checkTrue(maxSupportedKeySize != 0,
- "Configured " + MAX_DHGEX_KEY_SIZE_PROP + " value must be non-zero: %d", maxSupportedKeySize);
- }
-
- MAX_DHG_KEY_SIZE_HOLDER.set(maxSupportedKeySize);
- }
-
- return maxSupportedKeySize;
- }
-
- /**
- * Set programmatically the reported value for {@link #getMaxDHGroupExchangeKeySize()}
- * @param keySize The reported key size - if zero, then it will be auto-detected, if
- * negative then DH group exchange will be disabled
- */
- public static void setMaxDHGroupExchangeKeySize(int keySize) {
- synchronized (MAX_DHG_KEY_SIZE_HOLDER) {
- MAX_DHG_KEY_SIZE_HOLDER.set(keySize);
- }
- }
-
- public static boolean isDHGroupExchangeSupported(int maxKeySize) {
- ValidateUtils.checkTrue(maxKeySize > Byte.SIZE, "Invalid max. key size: %d", maxKeySize);
-
- try {
- BigInteger r = new BigInteger("0").setBit(maxKeySize - 1);
- DHParameterSpec dhSkipParamSpec = new DHParameterSpec(r, r);
- KeyPairGenerator kpg = getKeyPairGenerator("DH");
- kpg.initialize(dhSkipParamSpec);
- return true;
- } catch (GeneralSecurityException t) {
- return false;
- }
- }
-
- public static SecurityProviderChoice getDefaultProviderChoice() {
- SecurityProviderChoice choice;
- synchronized (DEFAULT_PROVIDER_HOLDER) {
- choice = DEFAULT_PROVIDER_HOLDER.get();
- if (choice != null) {
- return choice;
- }
-
- String name = System.getProperty(PROP_DEFAULT_SECURITY_PROVIDER);
- choice = (GenericUtils.isEmpty(name) || "none".equalsIgnoreCase(name))
- ? SecurityProviderChoice.EMPTY
- : SecurityProviderChoice.toSecurityProviderChoice(name);
- DEFAULT_PROVIDER_HOLDER.set(choice);
- }
-
- return choice;
- }
-
- public static void setDefaultProviderChoice(SecurityProviderChoice choice) {
- DEFAULT_PROVIDER_HOLDER.set(choice);
- }
-
- /**
- * @return A <U>copy</U> of the currently registered security providers
- */
- public static Set<String> getRegisteredProviders() {
- // returns a COPY of the providers in order to avoid modifications
- synchronized (REGISTERED_PROVIDERS) {
- return new TreeSet<>(REGISTERED_PROVIDERS.keySet());
- }
- }
-
- public static boolean isBouncyCastleRegistered() {
- register();
- return isProviderRegistered(BOUNCY_CASTLE);
- }
-
- public static boolean isProviderRegistered(String provider) {
- return getRegisteredProvider(provider) != null;
- }
-
- public static SecurityProviderRegistrar getRegisteredProvider(String provider) {
- ValidateUtils.checkNotNullAndNotEmpty(provider, "No provider name specified");
- synchronized (REGISTERED_PROVIDERS) {
- return REGISTERED_PROVIDERS.get(provider);
- }
- }
-
- public static boolean isRegistrationCompleted() {
- return REGISTRATION_STATE_HOLDER.get();
- }
-
- private static void register() {
- synchronized (REGISTRATION_STATE_HOLDER) {
- if (REGISTRATION_STATE_HOLDER.get()) {
- return;
- }
-
- String regsList = System.getProperty(SECURITY_PROVIDER_REGISTRARS,
- GenericUtils.join(DEFAULT_SECURITY_PROVIDER_REGISTRARS, ','));
- boolean bouncyCastleRegistered = false;
- if ((GenericUtils.length(regsList) > 0) && (!"none".equalsIgnoreCase(regsList))) {
- String[] classes = GenericUtils.split(regsList, ',');
- Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
- boolean debugEnabled = logger.isDebugEnabled();
- for (String registrarClass : classes) {
- SecurityProviderRegistrar r;
- try {
- r = ThreadUtils.createDefaultInstance(SecurityUtils.class, SecurityProviderRegistrar.class, registrarClass);
- } catch (ReflectiveOperationException t) {
- Throwable e = GenericUtils.peelException(t);
- logger.error("Failed ({}) to create default {} registrar instance: {}",
- e.getClass().getSimpleName(), registrarClass, e.getMessage());
- if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- } else if (e instanceof Error) {
- throw (Error) e;
- } else {
- throw new RuntimeException(e);
- }
- }
-
- String name = r.getName();
- SecurityProviderRegistrar registeredInstance = registerSecurityProvider(r);
- if (registeredInstance == null) {
- if (debugEnabled) {
- logger.debug("register({}) not registered - enabled={}, supported={}",
- name, r.isEnabled(), r.isSupported());
- }
- continue; // provider not registered - e.g., disabled, not supported
- }
-
- if (BOUNCY_CASTLE.equalsIgnoreCase(name)) {
- bouncyCastleRegistered = true;
- }
- }
- }
-
- SecurityProviderChoice choice = getDefaultProviderChoice();
- if (((choice == null) || (choice == SecurityProviderChoice.EMPTY)) && bouncyCastleRegistered) {
- setDefaultProviderChoice(SecurityProviderChoice.toSecurityProviderChoice(BOUNCY_CASTLE));
- }
-
- REGISTRATION_STATE_HOLDER.set(true);
- }
- }
-
- /**
- * @param registrar The registrar instance to register
- * @return The registered instance - may be different than required
- * if already registered. Returns {@code null} if not already registered
- * and not enabled or not supported registrar.
- */
- public static SecurityProviderRegistrar registerSecurityProvider(SecurityProviderRegistrar registrar) {
- Objects.requireNonNull(registrar, "No registrar instance to register");
- String name = registrar.getName();
- SecurityProviderRegistrar registeredInstance = getRegisteredProvider(name);
- if ((registeredInstance == null) && registrar.isEnabled() && registrar.isSupported()) {
- try {
- SecurityProviderRegistrar.registerSecurityProvider(registrar);
- synchronized (REGISTERED_PROVIDERS) {
- REGISTERED_PROVIDERS.put(name, registrar);
- }
-
- return registrar;
- } catch (Throwable t) {
- Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
- logger.error("Failed {} to register {} as a JCE provider: {}",
- t.getClass().getSimpleName(), name, t.getMessage());
- throw new RuntimeException("Failed to register " + name + " as a JCE provider", t);
- }
- }
-
- return registeredInstance;
- }
-
- ///////////////// Bouncycastle specific implementations //////////////////
-
- /* -------------------------------------------------------------------- */
-
- /**
- * @param resourceKey An identifier of the key being loaded - used as
- * argument to the {@link FilePasswordProvider#getPassword(String)}
- * invocation
- * @param inputStream The {@link InputStream} for the <U>private</U> key
- * @param provider A {@link FilePasswordProvider} - may be {@code null}
- * if the loaded key is <U>guaranteed</U> not to be encrypted
- * @return The loaded {@link KeyPair}
- * @throws IOException If failed to read/parse the input stream
- * @throws GeneralSecurityException If failed to generate the keys
- */
- public static KeyPair loadKeyPairIdentity(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
- throws IOException, GeneralSecurityException {
- KeyPairResourceParser parser = getKeyPairResourceParser();
- if (parser == null) {
- throw new NoSuchProviderException("No registered key-pair resource parser");
- }
-
- Collection<KeyPair> ids = parser.loadKeyPairs(resourceKey, provider, inputStream);
- int numLoaded = GenericUtils.size(ids);
- if (numLoaded <= 0) {
- throw new InvalidKeyException("Unsupported private key file format: " + resourceKey);
- }
- if (numLoaded != 1) {
- throw new InvalidKeySpecException("Multiple private key pairs N/A: " + resourceKey);
- }
-
- return ids.iterator().next();
- }
-
- /* -------------------------------------------------------------------- */
-
- public static AbstractGeneratorHostKeyProvider createGeneratorHostKeyProvider(Path path) {
- ValidateUtils.checkTrue(isBouncyCastleRegistered(), "BouncyCastle not registered");
- return new BouncyCastleGeneratorHostKeyProvider(path);
- }
-
- public static KeyPairResourceParser getBouncycastleKeyPairResourceParser() {
- ValidateUtils.checkTrue(isBouncyCastleRegistered(), "BouncyCastle not registered");
- return BouncyCastleKeyPairResourceParser.INSTANCE;
- }
-
- /**
- * @return If {@link #isBouncyCastleRegistered()} then a {@link BouncyCastleRandomFactory}
- * instance, otherwise a {@link JceRandomFactory} one
- */
- public static RandomFactory getRandomFactory() {
- if (isBouncyCastleRegistered()) {
- return BouncyCastleRandomFactory.INSTANCE;
- } else {
- return JceRandomFactory.INSTANCE;
- }
- }
-
- ///////////////////////////// ED25519 support ///////////////////////////////
-
- /**
- * @return {@code true} if EDDSA curves (e.g., {@code ed25519}) are supported
- */
- public static boolean isEDDSACurveSupported() {
- register();
-
- SecurityProviderRegistrar r = getRegisteredProvider(EDDSA);
- return (r != null) && r.isEnabled() && r.isSupported();
- }
-
- /* -------------------------------------------------------------------- */
-
- public static PublicKeyEntryDecoder<? extends PublicKey, ? extends PrivateKey> getEDDSAPublicKeyEntryDecoder() {
- if (!isEDDSACurveSupported()) {
- throw new UnsupportedOperationException(EDDSA + " provider N/A");
- }
-
- return EdDSASecurityProviderUtils.getEDDSAPublicKeyEntryDecoder();
- }
-
- public static PrivateKeyEntryDecoder<? extends PublicKey, ? extends PrivateKey> getOpenSSHEDDSAPrivateKeyEntryDecoder() {
- if (!isEDDSACurveSupported()) {
- throw new UnsupportedOperationException(EDDSA + " provider N/A");
- }
-
- return EdDSASecurityProviderUtils.getOpenSSHEDDSAPrivateKeyEntryDecoder();
- }
-
- public static org.apache.sshd.common.signature.Signature getEDDSASigner() {
- if (isEDDSACurveSupported()) {
- return EdDSASecurityProviderUtils.getEDDSASignature();
- }
-
- throw new UnsupportedOperationException(EDDSA + " Signer not available");
- }
-
- public static int getEDDSAKeySize(Key key) {
- return EdDSASecurityProviderUtils.getEDDSAKeySize(key);
- }
-
- public static Class<? extends PublicKey> getEDDSAPublicKeyType() {
- return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.getEDDSAPublicKeyType() : PublicKey.class;
- }
-
- public static Class<? extends PrivateKey> getEDDSAPrivateKeyType() {
- return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.getEDDSAPrivateKeyType() : PrivateKey.class;
- }
-
- public static boolean compareEDDSAPPublicKeys(PublicKey k1, PublicKey k2) {
- return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.compareEDDSAPPublicKeys(k1, k2) : false;
- }
-
- public static boolean compareEDDSAPrivateKeys(PrivateKey k1, PrivateKey k2) {
- return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.compareEDDSAPrivateKeys(k1, k2) : false;
- }
-
- public static PublicKey recoverEDDSAPublicKey(PrivateKey key) throws GeneralSecurityException {
- if (!isEDDSACurveSupported()) {
- throw new NoSuchAlgorithmException(EDDSA + " provider not supported");
- }
-
- return EdDSASecurityProviderUtils.recoverEDDSAPublicKey(key);
- }
-
- public static PublicKey generateEDDSAPublicKey(String keyType, byte[] seed) throws GeneralSecurityException {
- if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
- throw new InvalidKeyException("Unsupported key type: " + keyType);
- }
-
- if (!isEDDSACurveSupported()) {
- throw new NoSuchAlgorithmException(EDDSA + " provider not supported");
- }
-
- return EdDSASecurityProviderUtils.generateEDDSAPublicKey(seed);
- }
-
- public static <B extends Buffer> B putRawEDDSAPublicKey(B buffer, PublicKey key) {
- if (!isEDDSACurveSupported()) {
- throw new UnsupportedOperationException(EDDSA + " provider not supported");
- }
-
- return EdDSASecurityProviderUtils.putRawEDDSAPublicKey(buffer, key);
- }
-
- public static <B extends Buffer> B putEDDSAKeyPair(B buffer, KeyPair kp) {
- return putEDDSAKeyPair(buffer, Objects.requireNonNull(kp, "No key pair").getPublic(), kp.getPrivate());
- }
-
- public static <B extends Buffer> B putEDDSAKeyPair(B buffer, PublicKey pubKey, PrivateKey prvKey) {
- if (!isEDDSACurveSupported()) {
- throw new UnsupportedOperationException(EDDSA + " provider not supported");
- }
-
- return EdDSASecurityProviderUtils.putEDDSAKeyPair(buffer, pubKey, prvKey);
- }
-
- public static KeyPair extractEDDSAKeyPair(Buffer buffer, String keyType) throws GeneralSecurityException {
- if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
- throw new InvalidKeyException("Unsupported key type: " + keyType);
- }
-
- if (!isEDDSACurveSupported()) {
- throw new NoSuchAlgorithmException(EDDSA + " provider not supported");
- }
-
- throw new GeneralSecurityException("Full SSHD-440 implementation N/A");
- }
-
- //////////////////////////////////////////////////////////////////////////
-
- public static KeyPairResourceParser getKeyPairResourceParser() {
- KeyPairResourceParser parser;
- synchronized (KEYPAIRS_PARSER_HODLER) {
- parser = KEYPAIRS_PARSER_HODLER.get();
- if (parser != null) {
- return parser;
- }
-
- parser = KeyPairResourceParser.aggregate(
- PEMResourceParserUtils.PROXY,
- OpenSSHKeyPairResourceParser.INSTANCE);
- KEYPAIRS_PARSER_HODLER.set(parser);
- }
-
- return parser;
- }
-
- /**
- * @param parser The system-wide {@code KeyPairResourceParser} to use.
- * If set to {@code null}, then the default parser will be re-constructed
- * on next call to {@link #getKeyPairResourceParser()}
- */
- public static void setKeyPairResourceParser(KeyPairResourceParser parser) {
- synchronized (KEYPAIRS_PARSER_HODLER) {
- KEYPAIRS_PARSER_HODLER.set(parser);
- }
- }
-
- //////////////////////////// Security entities factories /////////////////////////////
-
- @SuppressWarnings("unchecked")
- public static <T> SecurityEntityFactory<T> resolveSecurityEntityFactory(
- Class<T> entityType, String algorithm, Predicate<? super SecurityProviderRegistrar> entitySelector) {
- Map<String, SecurityEntityFactory<?>> factoriesMap;
- synchronized (SECURITY_ENTITY_FACTORIES) {
- factoriesMap =
- SECURITY_ENTITY_FACTORIES.computeIfAbsent(
- entityType, k -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
- }
-
- String effectiveName = SecurityProviderRegistrar.getEffectiveSecurityEntityName(entityType, algorithm);
- SecurityEntityFactory<?> factoryEntry;
- synchronized (factoriesMap) {
- factoryEntry =
- factoriesMap.computeIfAbsent(
- effectiveName, k -> createSecurityEntityFactory(entityType, entitySelector));
- }
-
- return (SecurityEntityFactory<T>) factoryEntry;
- }
-
- public static <T> SecurityEntityFactory<T> createSecurityEntityFactory(
- Class<T> entityType, Predicate<? super SecurityProviderRegistrar> entitySelector) {
- register();
-
- SecurityProviderRegistrar registrar;
- synchronized (REGISTERED_PROVIDERS) {
- registrar =
- SecurityProviderRegistrar.findSecurityProviderRegistrarBySecurityEntity(
- entitySelector, REGISTERED_PROVIDERS.values());
- }
-
- try {
- return SecurityEntityFactory.toFactory(entityType, registrar, getDefaultProviderChoice());
- } catch (ReflectiveOperationException t) {
- Throwable e = GenericUtils.peelException(t);
- if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- } else if (e instanceof Error) {
- throw (Error) e;
- } else {
- throw new RuntimeException(e);
- }
- }
- }
-
- public static KeyFactory getKeyFactory(String algorithm) throws GeneralSecurityException {
- SecurityEntityFactory<KeyFactory> factory =
- resolveSecurityEntityFactory(KeyFactory.class, algorithm, r -> r.isKeyFactorySupported(algorithm));
- return factory.getInstance(algorithm);
- }
-
- public static Cipher getCipher(String transformation) throws GeneralSecurityException {
- SecurityEntityFactory<Cipher> factory =
- resolveSecurityEntityFactory(Cipher.class, transformation, r -> r.isCipherSupported(transformation));
- return factory.getInstance(transformation);
- }
-
- public static MessageDigest getMessageDigest(String algorithm) throws GeneralSecurityException {
- SecurityEntityFactory<MessageDigest> factory =
- resolveSecurityEntityFactory(MessageDigest.class, algorithm, r -> r.isMessageDigestSupported(algorithm));
- return factory.getInstance(algorithm);
- }
-
- public static KeyPairGenerator getKeyPairGenerator(String algorithm) throws GeneralSecurityException {
- SecurityEntityFactory<KeyPairGenerator> factory =
- resolveSecurityEntityFactory(KeyPairGenerator.class, algorithm, r -> r.isKeyPairGeneratorSupported(algorithm));
- return factory.getInstance(algorithm);
- }
-
- public static KeyAgreement getKeyAgreement(String algorithm) throws GeneralSecurityException {
- SecurityEntityFactory<KeyAgreement> factory =
- resolveSecurityEntityFactory(KeyAgreement.class, algorithm, r -> r.isKeyAgreementSupported(algorithm));
- return factory.getInstance(algorithm);
- }
-
- public static Mac getMac(String algorithm) throws GeneralSecurityException {
- SecurityEntityFactory<Mac> factory =
- resolveSecurityEntityFactory(Mac.class, algorithm, r -> r.isMacSupported(algorithm));
- return factory.getInstance(algorithm);
- }
-
- public static Signature getSignature(String algorithm) throws GeneralSecurityException {
- SecurityEntityFactory<Signature> factory =
- resolveSecurityEntityFactory(Signature.class, algorithm, r -> r.isSignatureSupported(algorithm));
- return factory.getInstance(algorithm);
- }
-
- public static CertificateFactory getCertificateFactory(String type) throws GeneralSecurityException {
- SecurityEntityFactory<CertificateFactory> factory =
- resolveSecurityEntityFactory(CertificateFactory.class, type, r -> r.isCertificateFactorySupported(type));
- return factory.getInstance(type);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java
deleted file mode 100644
index 3716719..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleGeneratorHostKeyProvider.java
+++ /dev/null
@@ -1,48 +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.util.security.bouncycastle;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-
-import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BouncyCastleGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProvider {
- public BouncyCastleGeneratorHostKeyProvider(Path path) {
- setPath(path);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
- try (org.bouncycastle.openssl.PEMWriter w =
- new org.bouncycastle.openssl.PEMWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {
- w.writeObject(kp);
- w.flush();
- }
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
deleted file mode 100644
index 4c8722a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
+++ /dev/null
@@ -1,130 +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.util.security.bouncycastle;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.NoSuchProviderException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityProviderRegistrar;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.bouncycastle.openssl.PEMDecryptorProvider;
-import org.bouncycastle.openssl.PEMEncryptedKeyPair;
-import org.bouncycastle.openssl.PEMKeyPair;
-import org.bouncycastle.openssl.PEMParser;
-import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
-import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourceParser {
- public static final List<String> BEGINNERS =
- Collections.unmodifiableList(
- Arrays.asList(
- "BEGIN RSA PRIVATE KEY",
- "BEGIN DSA PRIVATE KEY",
- "BEGIN EC PRIVATE KEY"));
-
- public static final List<String> ENDERS =
- Collections.unmodifiableList(
- Arrays.asList(
- "END RSA PRIVATE KEY",
- "END DSA PRIVATE KEY",
- "END EC PRIVATE KEY"));
-
- public static final BouncyCastleKeyPairResourceParser INSTANCE = new BouncyCastleKeyPairResourceParser();
-
- public BouncyCastleKeyPairResourceParser() {
- super(BEGINNERS, ENDERS);
- }
-
- @Override
- public Collection<KeyPair> extractKeyPairs(
- String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
- throws IOException, GeneralSecurityException {
- StringBuilder writer = new StringBuilder(beginMarker.length() + endMarker.length() + lines.size() * 80);
- writer.append(beginMarker).append(IoUtils.EOL);
- lines.forEach(l -> writer.append(l).append(IoUtils.EOL));
- writer.append(endMarker).append(IoUtils.EOL);
-
- String data = writer.toString();
- byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
- try (InputStream bais = new ByteArrayInputStream(dataBytes)) {
- return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, bais);
- }
- }
-
- @Override
- public Collection<KeyPair> extractKeyPairs(
- String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
- KeyPair kp = loadKeyPair(resourceKey, stream, passwordProvider);
- return (kp == null) ? Collections.emptyList() : Collections.singletonList(kp);
- }
-
- public static KeyPair loadKeyPair(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
- throws IOException, GeneralSecurityException {
- try (PEMParser r = new PEMParser(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
- Object o = r.readObject();
-
- SecurityProviderRegistrar registrar = SecurityUtils.getRegisteredProvider(SecurityUtils.BOUNCY_CASTLE);
- if (registrar == null) {
- throw new NoSuchProviderException(SecurityUtils.BOUNCY_CASTLE + " registrar not available");
- }
-
- JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
- if (registrar.isNamedProviderUsed()) {
- pemConverter.setProvider(registrar.getName());
- } else {
- pemConverter.setProvider(registrar.getSecurityProvider());
- }
- if (o instanceof PEMEncryptedKeyPair) {
- ValidateUtils.checkNotNull(provider, "No password provider for resource=%s", resourceKey);
-
- String password = ValidateUtils.checkNotNullAndNotEmpty(provider.getPassword(resourceKey), "No password provided for resource=%s", resourceKey);
- JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder();
- PEMDecryptorProvider pemDecryptor = decryptorBuilder.build(password.toCharArray());
- o = ((PEMEncryptedKeyPair) o).decryptKeyPair(pemDecryptor);
- }
-
- if (o instanceof PEMKeyPair) {
- return pemConverter.getKeyPair((PEMKeyPair) o);
- } else if (o instanceof KeyPair) {
- return (KeyPair) o;
- } else {
- throw new IOException("Failed to read " + resourceKey + " - unknown result object: " + o);
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandom.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandom.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandom.java
deleted file mode 100644
index 36c23e7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandom.java
+++ /dev/null
@@ -1,88 +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.util.security.bouncycastle;
-
-import java.security.SecureRandom;
-
-import org.apache.sshd.common.random.AbstractRandom;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.bouncycastle.crypto.prng.RandomGenerator;
-import org.bouncycastle.crypto.prng.VMPCRandomGenerator;
-
-/**
- * BouncyCastle <code>Random</code>.
- * This pseudo random number generator uses the a very fast PRNG from BouncyCastle.
- * The JRE random will be used when creating a new generator to add some random
- * data to the seed.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class BouncyCastleRandom extends AbstractRandom {
- public static final String NAME = SecurityUtils.BOUNCY_CASTLE;
- private final RandomGenerator random;
-
- public BouncyCastleRandom() {
- ValidateUtils.checkTrue(SecurityUtils.isBouncyCastleRegistered(), "BouncyCastle not registered");
- this.random = new VMPCRandomGenerator();
- byte[] seed = new SecureRandom().generateSeed(8);
- this.random.addSeedMaterial(seed);
- }
-
- @Override
- public String getName() {
- return NAME;
- }
-
- @Override
- public void fill(byte[] bytes, int start, int len) {
- this.random.nextBytes(bytes, start, len);
- }
-
- /**
- * Returns a pseudo-random uniformly distributed {@code int}
- * in the half-open range [0, n).
- */
- @Override
- public int random(int n) {
- ValidateUtils.checkTrue(n > 0, "Limit must be positive: %d", n);
- if ((n & -n) == n) {
- return (int) ((n * (long) next(31)) >> 31);
- }
-
- int bits;
- int val;
- do {
- bits = next(31);
- val = bits % n;
- } while (bits - val + (n - 1) < 0);
- return val;
- }
-
- private int next(int numBits) {
- int bytes = (numBits + 7) / 8;
- byte next[] = new byte[bytes];
- int ret = 0;
- random.nextBytes(next);
- for (int i = 0; i < bytes; i++) {
- ret = (next[i] & 0xFF) | (ret << 8);
- }
- return ret >>> (bytes * 8 - numBits);
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandomFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandomFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandomFactory.java
deleted file mode 100644
index 720c7a5..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleRandomFactory.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.util.security.bouncycastle;
-
-import org.apache.sshd.common.random.AbstractRandomFactory;
-import org.apache.sshd.common.random.Random;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Named factory for the BouncyCastle <code>Random</code>
- */
-public final class BouncyCastleRandomFactory extends AbstractRandomFactory {
- public static final String NAME = "bouncycastle";
- public static final BouncyCastleRandomFactory INSTANCE = new BouncyCastleRandomFactory();
-
- public BouncyCastleRandomFactory() {
- super(NAME);
- }
-
- @Override
- public boolean isSupported() {
- return SecurityUtils.isBouncyCastleRegistered();
- }
-
- @Override
- public Random create() {
- return new BouncyCastleRandom();
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java
deleted file mode 100644
index b47ca80..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleSecurityProviderRegistrar.java
+++ /dev/null
@@ -1,128 +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.util.security.bouncycastle;
-
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.security.Provider;
-import java.security.Signature;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ReflectionUtils;
-import org.apache.sshd.common.util.security.AbstractSecurityProviderRegistrar;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BouncyCastleSecurityProviderRegistrar extends AbstractSecurityProviderRegistrar {
- // We want to use reflection API so as not to require BouncyCastle to be present in the classpath
- public static final String PROVIDER_CLASS = "org.bouncycastle.jce.provider.BouncyCastleProvider";
- // Do not define a static registrar instance to minimize class loading issues
- private final AtomicReference<Boolean> supportHolder = new AtomicReference<>(null);
- private final AtomicReference<String> allSupportHolder = new AtomicReference<>();
-
- public BouncyCastleSecurityProviderRegistrar() {
- super(SecurityUtils.BOUNCY_CASTLE);
- }
-
- @Override
- public boolean isEnabled() {
- if (!super.isEnabled()) {
- return false;
- }
-
- // For backward compatibility
- return this.getBooleanProperty(SecurityUtils.REGISTER_BOUNCY_CASTLE_PROP, true);
- }
-
- @Override
- public Provider getSecurityProvider() {
- try {
- return getOrCreateProvider(PROVIDER_CLASS);
- } catch (ReflectiveOperationException t) {
- Throwable e = GenericUtils.peelException(t);
- log.error("getSecurityProvider({}) failed ({}) to instantiate {}: {}",
- getName(), e.getClass().getSimpleName(), PROVIDER_CLASS, e.getMessage());
- if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- }
-
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public String getDefaultSecurityEntitySupportValue(Class<?> entityType) {
- String allValue = allSupportHolder.get();
- if (GenericUtils.length(allValue) > 0) {
- return allValue;
- }
-
- String propName = getConfigurationPropertyName("supportAll");
- allValue = this.getStringProperty(propName, ALL_OPTIONS_VALUE);
- if (GenericUtils.isEmpty(allValue)) {
- allValue = NO_OPTIONS_VALUE;
- }
-
- allSupportHolder.set(allValue);
- return allValue;
- }
-
- @Override
- public boolean isSecurityEntitySupported(Class<?> entityType, String name) {
- if (!isSupported()) {
- return false;
- }
-
- // Some known values it does not support
- if (KeyPairGenerator.class.isAssignableFrom(entityType)
- || KeyFactory.class.isAssignableFrom(entityType)) {
- if (Objects.compare(name, SecurityUtils.EDDSA, String.CASE_INSENSITIVE_ORDER) == 0) {
- return false;
- }
- } else if (Signature.class.isAssignableFrom(entityType)) {
- if (Objects.compare(name, SecurityUtils.CURVE_ED25519_SHA512, String.CASE_INSENSITIVE_ORDER) == 0) {
- return false;
- }
- }
-
- return super.isSecurityEntitySupported(entityType, name);
- }
-
- @Override
- public boolean isSupported() {
- Boolean supported;
- synchronized (supportHolder) {
- supported = supportHolder.get();
- if (supported != null) {
- return supported.booleanValue();
- }
-
- ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(getClass());
- supported = ReflectionUtils.isClassAvailable(cl, PROVIDER_CLASS);
- supportHolder.set(supported);
- }
-
- return supported.booleanValue();
- }
-}
[25/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolver.java b/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolver.java
deleted file mode 100644
index c333c7f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolver.java
+++ /dev/null
@@ -1,124 +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;
-
-import java.util.Collections;
-import java.util.Map;
-
-/**
- * Indicates an entity that can be configured using properties. The properties
- * are simple name-value pairs where the actual value type depends on the
- * property. Some automatic conversions may be available - e.g., from a string
- * to a numeric or {@code boolean} value, or from {@code int} to {@code long},
- * etc.. <B>Note:</B> implementations may decide to use case <U>insensitive</U>
- * property names, therefore it is <U><B>highly discouraged</B></U> to use names
- * that differ from each other only in case sensitivity. Also, implementations
- * may choose to trim whitespaces, thus such are also highly discouraged.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface PropertyResolver {
- /**
- * An "empty" resolver with no properties and no parent
- */
- PropertyResolver EMPTY = new PropertyResolver() {
- @Override
- public PropertyResolver getParentPropertyResolver() {
- return null;
- }
-
- @Override
- public Map<String, Object> getProperties() {
- return Collections.emptyMap();
- }
-
- @Override
- public String toString() {
- return "EMPTY";
- }
- };
-
- /**
- * @return The parent resolver that can be used to query for missing
- * properties - {@code null} if no parent
- */
- PropertyResolver getParentPropertyResolver();
-
- /**
- * <P>
- * A map of properties that can be used to configure the SSH server or
- * client. This map will never be changed by either the server or client and
- * is not supposed to be changed at runtime (changes are not bound to have
- * any effect on a running client or server), though it may affect the
- * creation of sessions later as these values are usually not cached.
- * </P>
- *
- * <P>
- * <B>Note:</B> the <U>type</U> of the mapped property should match the
- * expected configuration value type - {@code Long, Integer, Boolean,
- * String}, etc.... If it doesn't, the {@code toString()} result of the
- * mapped value is used to convert it to the required type. E.g., if the
- * mapped value is the <U>string</U> "1234" and the expected value
- * is a {@code long} then it will be parsed into one. Also, if the mapped
- * value is an {@code Integer} but a {@code long} is expected, then it will
- * be converted into one.
- * </P>
- *
- * @return a valid <code>Map</code> containing configuration values, never
- * {@code null}
- */
- Map<String, Object> getProperties();
-
- default long getLongProperty(String name, long def) {
- return PropertyResolverUtils.getLongProperty(this, name, def);
- }
-
- default Long getLong(String name) {
- return PropertyResolverUtils.getLong(this, name);
- }
-
- default int getIntProperty(String name, int def) {
- return PropertyResolverUtils.getIntProperty(this, name, def);
- }
-
- default Integer getInteger(String name) {
- return PropertyResolverUtils.getInteger(this, name);
- }
-
- default boolean getBooleanProperty(String name, boolean def) {
- return PropertyResolverUtils.getBooleanProperty(this, name, def);
- }
-
- default Boolean getBoolean(String name) {
- return PropertyResolverUtils.getBoolean(this, name);
- }
-
- default String getStringProperty(String name, String def) {
- return PropertyResolverUtils.getStringProperty(this, name, def);
- }
-
- default String getString(String name) {
- return PropertyResolverUtils.getString(this, name);
- }
-
- default Object getObject(String name) {
- return PropertyResolverUtils.getObject(this, name);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
deleted file mode 100644
index 6677685..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java
+++ /dev/null
@@ -1,482 +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;
-
-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 "org.apache.sshd" 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-core/src/main/java/org/apache/sshd/common/RuntimeSshException.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/RuntimeSshException.java b/sshd-core/src/main/java/org/apache/sshd/common/RuntimeSshException.java
deleted file mode 100644
index 8c9164f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/RuntimeSshException.java
+++ /dev/null
@@ -1,48 +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;
-
-/**
- * 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-core/src/main/java/org/apache/sshd/common/SshConstants.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java b/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java
deleted file mode 100644
index 01e8ae7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java
+++ /dev/null
@@ -1,245 +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;
-
-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-core/src/main/java/org/apache/sshd/common/SshException.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/SshException.java b/sshd-core/src/main/java/org/apache/sshd/common/SshException.java
deleted file mode 100644
index 4280d08..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/SshException.java
+++ /dev/null
@@ -1,72 +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;
-
-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-core/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java b/sshd-core/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
deleted file mode 100644
index 4408be2..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/SyspropsMapWrapper.java
+++ /dev/null
@@ -1,209 +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;
-
-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 "pure" 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-core/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java b/sshd-core/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java
deleted file mode 100644
index 485f2f2..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/auth/MutableUserHolder.java
+++ /dev/null
@@ -1,27 +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.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-core/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java b/sshd-core/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
deleted file mode 100644
index 7ee76ad..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/auth/UsernameHolder.java
+++ /dev/null
@@ -1,32 +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.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-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
index 085bd41..a6c11ca 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
@@ -34,7 +34,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.IntUnaryOperator;
-import org.apache.sshd.common.AttributeStore;
import org.apache.sshd.common.Closeable;
import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.PropertyResolver;
@@ -964,11 +963,6 @@ public abstract class AbstractChannel
return (T) attributes.remove(Objects.requireNonNull(key, "No key"));
}
- @Override
- public <T> T resolveAttribute(AttributeKey<T> key) {
- return AttributeStore.resolveAttribute(this, key);
- }
-
protected void configureWindow() {
localWindow.init(this);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java
index 9aa0b0f..1ef143a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java
@@ -21,6 +21,7 @@ package org.apache.sshd.common.channel;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
+import java.util.Objects;
import org.apache.sshd.client.future.OpenFuture;
import org.apache.sshd.common.AttributeStore;
@@ -211,4 +212,29 @@ public interface Channel
* @throws IOException If failed to handle the success
*/
void handleOpenFailure(Buffer buffer) throws IOException;
+
+ @Override
+ default <T> T resolveAttribute(AttributeKey<T> key) {
+ return resolveAttribute(this, key);
+ }
+
+ /**
+ * Attempts to use the channel attribute, if not found then tries the session
+ *
+ * @param <T> The generic attribute type
+ * @param channel The {@link Channel} - ignored if {@code null}
+ * @param key The attribute key - never {@code null}
+ * @return Associated value - {@code null} if not found
+ * @see Session#getFactoryManager()
+ * @see #resolveAttribute(Session, AttributeKey)
+ */
+ static <T> T resolveAttribute(Channel channel, AttributeKey<T> key) {
+ Objects.requireNonNull(key, "No key");
+ if (channel == null) {
+ return null;
+ }
+
+ T value = channel.getAttribute(key);
+ return (value != null) ? value : Session.resolveAttribute(channel.getSession(), key);
+ }
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
deleted file mode 100644
index c3d9426..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseCipher.java
+++ /dev/null
@@ -1,113 +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.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-core/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java
deleted file mode 100644
index 2e3fc1e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BaseRC4Cipher.java
+++ /dev/null
@@ -1,54 +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.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-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
deleted file mode 100644
index 8609d50..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/BuiltinCiphers.java
+++ /dev/null
@@ -1,348 +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.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-core/src/main/java/org/apache/sshd/common/cipher/Cipher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/Cipher.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/Cipher.java
deleted file mode 100644
index 868e983..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/Cipher.java
+++ /dev/null
@@ -1,89 +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.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-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
deleted file mode 100644
index 36909f3..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherFactory.java
+++ /dev/null
@@ -1,31 +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.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-core/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherInformation.java
deleted file mode 100644
index f17fd16..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherInformation.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.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-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
deleted file mode 100644
index 15b6e9f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/CipherNone.java
+++ /dev/null
@@ -1,63 +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.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
- }
-}
[47/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/ECCurves.java b/sshd-common/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
new file mode 100644
index 0000000..ae39dd3
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
@@ -0,0 +1,580 @@
+/*
+ * 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.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.security.interfaces.ECKey;
+import java.security.spec.ECField;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.EllipticCurve;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.NavigableSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.digest.BuiltinDigests;
+import org.apache.sshd.common.digest.Digest;
+import org.apache.sshd.common.digest.DigestFactory;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Utilities for working with elliptic curves.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum ECCurves implements NamedResource, OptionalFeature {
+ nistp256(Constants.NISTP256, new int[]{1, 2, 840, 10045, 3, 1, 7},
+ new ECParameterSpec(
+ new EllipticCurve(
+ new ECFieldFp(new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16)),
+ new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16),
+ new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)),
+ new ECPoint(
+ new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16),
+ new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16)),
+ new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16),
+ 1),
+ 32,
+ BuiltinDigests.sha256),
+ nistp384(Constants.NISTP384, new int[]{1, 3, 132, 0, 34},
+ new ECParameterSpec(
+ new EllipticCurve(
+ new ECFieldFp(new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", 16)),
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", 16),
+ new BigInteger("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", 16)),
+ new ECPoint(
+ new BigInteger("AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", 16),
+ new BigInteger("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", 16)),
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", 16),
+ 1),
+ 48,
+ BuiltinDigests.sha384),
+ nistp521(Constants.NISTP521, new int[]{1, 3, 132, 0, 35},
+ new ECParameterSpec(
+ new EllipticCurve(
+ new ECFieldFp(new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)),
+ new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", 16),
+ new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951"
+ + "EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", 16)),
+ new ECPoint(
+ new BigInteger("00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77"
+ + "EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", 16),
+ new BigInteger("011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE7299"
+ + "5EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", 16)),
+ new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B"
+ + "7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16),
+ 1),
+ 66,
+ BuiltinDigests.sha512);
+
+ /**
+ * A {@link Set} of all the known curves
+ */
+ public static final Set<ECCurves> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(ECCurves.class));
+
+ /**
+ * A {@link Set} of all the known curves names
+ */
+ public static final NavigableSet<String> NAMES =
+ Collections.unmodifiableNavigableSet(GenericUtils.mapSort(
+ VALUES,
+ ECCurves::getName,
+ String.CASE_INSENSITIVE_ORDER));
+
+ /**
+ * A {@link Set} of all the known curves key types
+ */
+ public static final NavigableSet<String> KEY_TYPES =
+ Collections.unmodifiableNavigableSet(GenericUtils.mapSort(
+ VALUES,
+ ECCurves::getKeyType,
+ String.CASE_INSENSITIVE_ORDER));
+
+ public static final Comparator<ECCurves> BY_KEY_SIZE = (o1, o2) -> {
+ int k1 = (o1 == null) ? Integer.MAX_VALUE : o1.getKeySize();
+ int k2 = (o2 == null) ? Integer.MAX_VALUE : o2.getKeySize();
+ return Integer.compare(k1, k2);
+ };
+
+ public static final List<ECCurves> SORTED_KEY_SIZE =
+ Collections.unmodifiableList(VALUES.stream()
+ .sorted(BY_KEY_SIZE)
+ .collect(Collectors.toList()));
+
+ private final String name;
+ private final String keyType;
+ private final String oidString;
+ private final List<Integer> oidValue;
+ private final ECParameterSpec params;
+ private final int keySize;
+ private final int numOctets;
+ private final DigestFactory digestFactory;
+
+ ECCurves(String name, int[] oid, ECParameterSpec params, int numOctets, DigestFactory digestFactory) {
+ this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No curve name");
+ this.oidString = NumberUtils.join('.', ValidateUtils.checkNotNullAndNotEmpty(oid, "No OID"));
+ this.oidValue = Collections.unmodifiableList(NumberUtils.asList(oid));
+ this.keyType = Constants.ECDSA_SHA2_PREFIX + name;
+ this.params = ValidateUtils.checkNotNull(params, "No EC params for %s", name);
+ this.keySize = getCurveSize(params);
+ this.numOctets = numOctets;
+ this.digestFactory = Objects.requireNonNull(digestFactory, "No digestFactory");
+ }
+
+ @Override // The curve name
+ public final String getName() {
+ return name;
+ }
+
+ public final String getOID() {
+ return oidString;
+ }
+
+ public final List<Integer> getOIDValue() {
+ return oidValue;
+ }
+
+ /**
+ * @return The standard key type used to represent this curve
+ */
+ public final String getKeyType() {
+ return keyType;
+ }
+
+ @Override
+ public final boolean isSupported() {
+ return SecurityUtils.isECCSupported() && digestFactory.isSupported();
+ }
+
+ public final ECParameterSpec getParameters() {
+ return params;
+ }
+
+ /**
+ * @return The size (in bits) of the key
+ */
+ public final int getKeySize() {
+ return keySize;
+ }
+
+ /**
+ * @return The number of octets used to represent the point(s) for the curve
+ */
+ public final int getNumPointOctets() {
+ return numOctets;
+ }
+
+ /**
+ * @return The {@link Digest} to use when hashing the curve's parameters
+ */
+ public final Digest getDigestForParams() {
+ return digestFactory.create();
+ }
+
+ /**
+ * @param type The key type value - ignored if {@code null}/empty
+ * @return The matching {@link ECCurves} constant - {@code null} if
+ * no match found case <U>insensitive</U>
+ */
+ public static ECCurves fromKeyType(String type) {
+ if (GenericUtils.isEmpty(type)) {
+ return null;
+ }
+
+ for (ECCurves c : VALUES) {
+ if (type.equalsIgnoreCase(c.getKeyType())) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param name The curve name (case <U>insensitive</U> - ignored if
+ * {@code null}/empty
+ * @return The matching {@link ECCurves} instance - {@code null} if no
+ * match found
+ */
+ public static ECCurves fromCurveName(String name) {
+ return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+ }
+
+ /**
+ * @param key The {@link ECKey} - ignored if {@code null}
+ * @return The matching {@link ECCurves} instance - {@code null} if no
+ * match found
+ */
+ public static ECCurves fromECKey(ECKey key) {
+ return fromCurveParameters((key == null) ? null : key.getParams());
+ }
+
+ /**
+ * @param params The curve's {@link ECParameterSpec} - ignored if {@code null}
+ * @return The matching {@link ECCurves} value - {@code null} if no match found
+ * @see #getCurveSize(ECParameterSpec)
+ * @see #fromCurveSize(int)
+ */
+ public static ECCurves fromCurveParameters(ECParameterSpec params) {
+ if (params == null) {
+ return null;
+ } else {
+ return fromCurveSize(getCurveSize(params));
+ }
+ }
+
+ /**
+ * @param keySize The key size (in bits)
+ * @return The matching {@link ECCurves} value - {@code null} if no
+ * match found
+ */
+ public static ECCurves fromCurveSize(int keySize) {
+ if (keySize <= 0) {
+ return null;
+ }
+
+ for (ECCurves c : VALUES) {
+ if (keySize == c.getKeySize()) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ public static ECCurves fromOIDValue(List<? extends Number> oid) {
+ if (GenericUtils.isEmpty(oid)) {
+ return null;
+ }
+
+ for (ECCurves c : VALUES) {
+ List<? extends Number> v = c.getOIDValue();
+ if (oid.size() != v.size()) {
+ continue;
+ }
+
+ boolean matches = true;
+ for (int index = 0; index < v.size(); index++) {
+ Number exp = v.get(index);
+ Number act = oid.get(index);
+ if (exp.intValue() != act.intValue()) {
+ matches = false;
+ break;
+ }
+ }
+
+ if (matches) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ public static ECCurves fromOID(String oid) {
+ if (GenericUtils.isEmpty(oid)) {
+ return null;
+ }
+
+ for (ECCurves c : VALUES) {
+ if (oid.equalsIgnoreCase(c.getOID())) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param params The curve's {@link ECParameterSpec}
+ * @return The curve's key size in bits
+ * @throws IllegalArgumentException if invalid parameters provided
+ */
+ public static int getCurveSize(ECParameterSpec params) {
+ EllipticCurve curve = Objects.requireNonNull(params, "No EC params").getCurve();
+ ECField field = Objects.requireNonNull(curve, "No EC curve").getField();
+ return Objects.requireNonNull(field, "No EC field").getFieldSize();
+ }
+
+ public static byte[] encodeECPoint(ECPoint group, ECParameterSpec params) {
+ return encodeECPoint(group, params.getCurve());
+ }
+
+ public static byte[] encodeECPoint(ECPoint group, EllipticCurve curve) {
+ // M has len 2 ceil(log_2(q)/8) + 1 ?
+ int elementSize = (curve.getField().getFieldSize() + 7) / 8;
+ byte[] m = new byte[2 * elementSize + 1];
+
+ // Uncompressed format
+ m[0] = 0x04;
+
+ byte[] affineX = removeLeadingZeroes(group.getAffineX().toByteArray());
+ System.arraycopy(affineX, 0, m, 1 + elementSize - affineX.length, affineX.length);
+
+ byte[] affineY = removeLeadingZeroes(group.getAffineY().toByteArray());
+ System.arraycopy(affineY, 0, m, 1 + elementSize + elementSize - affineY.length, affineY.length);
+
+ return m;
+ }
+
+ private static byte[] removeLeadingZeroes(byte[] input) {
+ if (input[0] != 0x00) {
+ return input;
+ }
+
+ int pos = 1;
+ while (pos < input.length - 1 && input[pos] == 0x00) {
+ pos++;
+ }
+
+ byte[] output = new byte[input.length - pos];
+ System.arraycopy(input, pos, output, 0, output.length);
+ return output;
+ }
+
+ /**
+ * Converts the given octet string (defined by ASN.1 specifications) to a {@link BigInteger}
+ * As octet strings always represent positive integers, a zero-byte is prepended to
+ * the given array if necessary (if is MSB equal to 1), then this is converted to BigInteger
+ * The conversion is defined in the Section 2.3.8
+ *
+ * @param octets - octet string bytes to be converted
+ * @return The {@link BigInteger} representation of the octet string
+ */
+ public static BigInteger octetStringToInteger(byte... octets) {
+ if (octets == null) {
+ return null;
+ } else if (octets.length == 0) {
+ return BigInteger.ZERO;
+ } else {
+ return new BigInteger(1, octets);
+ }
+ }
+
+ public static ECPoint octetStringToEcPoint(byte... octets) {
+ if (NumberUtils.isEmpty(octets)) {
+ return null;
+ }
+
+ int startIndex = findFirstNonZeroIndex(octets);
+ if (startIndex < 0) {
+ throw new IllegalArgumentException("All zeroes ECPoint N/A");
+ }
+
+ byte indicator = octets[startIndex];
+ ECCurves.ECPointCompression compression = ECCurves.ECPointCompression.fromIndicatorValue(indicator);
+ if (compression == null) {
+ throw new UnsupportedOperationException("Unknown compression indicator value: 0x" + Integer.toHexString(indicator & 0xFF));
+ }
+
+ // The coordinates actually start after the compression indicator
+ return compression.octetStringToEcPoint(octets, startIndex + 1, octets.length - startIndex - 1);
+ }
+
+ private static int findFirstNonZeroIndex(byte... octets) {
+ if (NumberUtils.isEmpty(octets)) {
+ return -1;
+ }
+
+ for (int index = 0; index < octets.length; index++) {
+ if (octets[index] != 0) {
+ return index;
+ }
+ }
+
+ return -1; // all zeroes
+ }
+
+ public static final class Constants {
+ /**
+ * Standard prefix of NISTP key types when encoded
+ */
+ public static final String ECDSA_SHA2_PREFIX = "ecdsa-sha2-";
+
+ public static final String NISTP256 = "nistp256";
+ public static final String NISTP384 = "nistp384";
+ public static final String NISTP521 = "nistp521";
+
+ private Constants() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+ }
+
+ /**
+ * The various {@link ECPoint} representation compression indicators
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="https://www.ietf.org/rfc/rfc5480.txt">RFC-5480 - section 2.2</A>
+ */
+ public enum ECPointCompression {
+ // see http://tools.ietf.org/html/draft-jivsov-ecc-compact-00
+ // see http://crypto.stackexchange.com/questions/8914/ecdsa-compressed-public-key-point-back-to-uncompressed-public-key-point
+ VARIANT2((byte) 0x02) {
+ @Override
+ public ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len) {
+ byte[] xp = new byte[len];
+ System.arraycopy(octets, startIndex, xp, 0, len);
+ BigInteger x = octetStringToInteger(xp);
+
+ // TODO derive even Y...
+ throw new UnsupportedOperationException("octetStringToEcPoint(" + name() + ")(X=" + x + ") compression support N/A");
+ }
+ },
+ VARIANT3((byte) 0x03) {
+ @Override
+ public ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len) {
+ byte[] xp = new byte[len];
+ System.arraycopy(octets, startIndex, xp, 0, len);
+ BigInteger x = octetStringToInteger(xp);
+
+ // TODO derive odd Y...
+ throw new UnsupportedOperationException("octetStringToEcPoint(" + name() + ")(X=" + x + ") compression support N/A");
+ }
+ },
+ UNCOMPRESSED((byte) 0x04) {
+ @Override
+ public ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len) {
+ int numElements = len / 2; /* x, y */
+ if (len != (numElements * 2)) { // make sure length is not odd
+ throw new IllegalArgumentException("octetStringToEcPoint(" + name() + ") "
+ + " invalid remainder octets representation: "
+ + " expected=" + (2 * numElements) + ", actual=" + len);
+ }
+
+ byte[] xp = new byte[numElements];
+ byte[] yp = new byte[numElements];
+ System.arraycopy(octets, startIndex, xp, 0, numElements);
+ System.arraycopy(octets, startIndex + numElements, yp, 0, numElements);
+
+ BigInteger x = octetStringToInteger(xp);
+ BigInteger y = octetStringToInteger(yp);
+ return new ECPoint(x, y);
+ }
+
+ @Override
+ public void writeECPoint(OutputStream s, String curveName, ECPoint p) throws IOException {
+ ECCurves curve = fromCurveName(curveName);
+ if (curve == null) {
+ throw new StreamCorruptedException("writeECPoint(" + name() + ")[" + curveName + "] cannot determine octets count");
+ }
+
+ int numElements = curve.getNumPointOctets();
+ KeyEntryResolver.encodeInt(s, 1 /* the indicator */ + 2 * numElements);
+ s.write(getIndicatorValue());
+ writeCoordinate(s, "X", p.getAffineX(), numElements);
+ writeCoordinate(s, "Y", p.getAffineY(), numElements);
+ }
+ };
+
+ public static final Set<ECPointCompression> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(ECPointCompression.class));
+
+ private final byte indicatorValue;
+
+ ECPointCompression(byte indicator) {
+ indicatorValue = indicator;
+ }
+
+ public final byte getIndicatorValue() {
+ return indicatorValue;
+ }
+
+ public abstract ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len);
+
+ public byte[] ecPointToOctetString(String curveName, ECPoint p) {
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream((2 * 66) + Long.SIZE)) {
+ writeECPoint(baos, curveName, p);
+ return baos.toByteArray();
+ } catch (IOException e) {
+ throw new RuntimeException("ecPointToOctetString(" + curveName + ")"
+ + " failed (" + e.getClass().getSimpleName() + ")"
+ + " to write data: " + e.getMessage(),
+ e);
+ }
+ }
+
+ public void writeECPoint(OutputStream s, String curveName, ECPoint p) throws IOException {
+ if (s == null) {
+ throw new EOFException("No output stream");
+ }
+
+ throw new StreamCorruptedException("writeECPoint(" + name() + ")[" + p + "] N/A");
+ }
+
+ protected void writeCoordinate(OutputStream s, String n, BigInteger v, int numElements) throws IOException {
+ byte[] vp = v.toByteArray();
+ int startIndex = 0;
+ int vLen = vp.length;
+ if (vLen > numElements) {
+ if (vp[0] == 0) { // skip artificial positive sign
+ startIndex++;
+ vLen--;
+ }
+ }
+
+ if (vLen > numElements) {
+ throw new StreamCorruptedException("writeCoordinate(" + name() + ")[" + n + "]"
+ + " value length (" + vLen + ") exceeds max. (" + numElements + ")"
+ + " for " + v);
+ }
+
+ if (vLen < numElements) {
+ byte[] tmp = new byte[numElements];
+ System.arraycopy(vp, startIndex, tmp, numElements - vLen, vLen);
+ vp = tmp;
+ }
+
+ s.write(vp, startIndex, vLen);
+ }
+
+ public static ECPointCompression fromIndicatorValue(int value) {
+ if ((value < 0) || (value > 0xFF)) {
+ return null; // must be a byte value
+ }
+
+ for (ECPointCompression c : VALUES) {
+ if (value == c.getIndicatorValue()) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/cipher/package.html
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/cipher/package.html b/sshd-common/src/main/java/org/apache/sshd/common/cipher/package.html
new file mode 100644
index 0000000..197a89d
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/cipher/package.html
@@ -0,0 +1,26 @@
+<!--
+ 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.
+-->
+<html>
+<head>
+</head>
+<body>
+
+<a href="{@docRoot}/org/apache/sshd/common/cipher/Cipher.html"><code>Cipher</code></a>
+implementations.
+
+</body>
+</html>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/BaseCompression.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
new file mode 100644
index 0000000..ff31947
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/BaseCompression.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.compression;
+
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class BaseCompression implements Compression {
+ private final String name;
+
+ protected BaseCompression(String name) {
+ this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No compression name");
+ }
+
+ @Override
+ public final String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isCompressionExecuted() {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
new file mode 100644
index 0000000..49ad0ab
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.compression;
+
+import 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 org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.NamedFactoriesListParseResult;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum BuiltinCompressions implements CompressionFactory {
+ none(Constants.NONE) {
+ @Override
+ public Compression create() {
+ return new CompressionNone();
+ }
+
+ @Override
+ public boolean isCompressionExecuted() {
+ return false;
+ }
+ },
+ zlib(Constants.ZLIB) {
+ @Override
+ public Compression create() {
+ return new CompressionZlib();
+ }
+ },
+ delayedZlib(Constants.DELAYED_ZLIB) {
+ @Override
+ public Compression create() {
+ return new CompressionDelayedZlib();
+ }
+
+ @Override
+ public boolean isDelayed() {
+ return true;
+ }
+ };
+
+ public static final Set<BuiltinCompressions> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(BuiltinCompressions.class));
+
+ private static final Map<String, CompressionFactory> EXTENSIONS =
+ new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+ private final String name;
+
+ BuiltinCompressions(String n) {
+ name = n;
+ }
+
+ @Override
+ public final String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isDelayed() {
+ return false;
+ }
+
+ @Override
+ public boolean isCompressionExecuted() {
+ return true;
+ }
+
+ @Override
+ public final String toString() {
+ return getName();
+ }
+
+ @Override
+ public final boolean isSupported() {
+ return true;
+ }
+
+ /**
+ * Registered a {@link org.apache.sshd.common.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(CompressionFactory 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<CompressionFactory> 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 CompressionFactory unregisterExtension(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ synchronized (EXTENSIONS) {
+ return EXTENSIONS.remove(name);
+ }
+ }
+
+ public static BuiltinCompressions fromFactoryName(String name) {
+ return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+ }
+
+ /**
+ * @param compressions A comma-separated list of Compressions' names - ignored
+ * if {@code null}/empty
+ * @return A {@link ParseResult} containing the successfully parsed
+ * factories and the unknown ones. <B>Note:</B> it is up to caller to
+ * ensure that the lists do not contain duplicates
+ */
+ public static ParseResult parseCompressionsList(String compressions) {
+ return parseCompressionsList(GenericUtils.split(compressions, ','));
+ }
+
+ public static ParseResult parseCompressionsList(String... compressions) {
+ return parseCompressionsList(GenericUtils.isEmpty((Object[]) compressions) ? Collections.emptyList() : Arrays.asList(compressions));
+ }
+
+ public static ParseResult parseCompressionsList(Collection<String> compressions) {
+ if (GenericUtils.isEmpty(compressions)) {
+ return ParseResult.EMPTY;
+ }
+
+ List<CompressionFactory> factories = new ArrayList<>(compressions.size());
+ List<String> unknown = Collections.emptyList();
+ for (String name : compressions) {
+ CompressionFactory c = resolveFactory(name);
+ if (c != null) {
+ factories.add(c);
+ } else {
+ // replace the (unmodifiable) empty list with a real one
+ if (unknown.isEmpty()) {
+ unknown = new ArrayList<>();
+ }
+ 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 CompressionFactory resolveFactory(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return null;
+ }
+
+ CompressionFactory c = fromFactoryName(name);
+ if (c != null) {
+ return c;
+ }
+
+ synchronized (EXTENSIONS) {
+ return EXTENSIONS.get(name);
+ }
+ }
+
+ /**
+ * Holds the result of {@link BuiltinCompressions#parseCompressionsList(String)}
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+ public static class ParseResult extends NamedFactoriesListParseResult<Compression, CompressionFactory> {
+ public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
+
+ public ParseResult(List<CompressionFactory> parsed, List<String> unsupported) {
+ super(parsed, unsupported);
+ }
+ }
+
+ public static final class Constants {
+ public static final String NONE = "none";
+ public static final String ZLIB = "zlib";
+ public static final String DELAYED_ZLIB = "zlib@openssh.com";
+
+ private Constants() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/Compression.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/Compression.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/Compression.java
new file mode 100644
index 0000000..91ac27e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/Compression.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.compression;
+
+import java.io.IOException;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * Interface used to compress the stream of data between the
+ * SSH server and clients.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Compression extends CompressionInformation {
+
+ /**
+ * Enum identifying if this object will be used to compress
+ * or uncompress data.
+ */
+ enum Type {
+ Inflater,
+ Deflater
+ }
+
+ /**
+ * Initialize this object to either compress or uncompress data.
+ * This method must be called prior to any calls to either
+ * <code>compress</code> or <code>uncompress</code>.
+ * Once the object has been initialized, only one of
+ * <code>compress</code> or <code>uncompress</code> methods can be
+ * called.
+ *
+ * @param type compression type
+ * @param level compression level
+ */
+ void init(Type type, int level);
+
+ /**
+ * Compress the given buffer in place.
+ *
+ * @param buffer the buffer containing the data to compress
+ * @throws IOException if an error occurs
+ */
+ void compress(Buffer buffer) throws IOException;
+
+ /**
+ * Uncompress the data in a buffer into another buffer.
+ *
+ * @param from the buffer containing the data to uncompress
+ * @param to the buffer receiving the uncompressed data
+ * @throws IOException if an error occurs
+ */
+ void uncompress(Buffer from, Buffer to) throws IOException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
new file mode 100644
index 0000000..c062e38
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.compression;
+
+
+/**
+ * ZLib delayed compression.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see Compression#isDelayed()
+ */
+public class CompressionDelayedZlib extends CompressionZlib {
+ /**
+ * Create a new instance of a delayed ZLib compression
+ */
+ public CompressionDelayedZlib() {
+ super(BuiltinCompressions.Constants.DELAYED_ZLIB);
+ }
+
+ @Override
+ public boolean isDelayed() {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
new file mode 100644
index 0000000..355594a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionFactory.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.compression;
+
+import org.apache.sshd.common.BuiltinFactory;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+// CHECKSTYLE:OFF
+public interface CompressionFactory extends BuiltinFactory<Compression>, CompressionInformation {
+ // 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/compression/CompressionInformation.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java
new file mode 100644
index 0000000..428689f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.compression;
+
+import org.apache.sshd.common.NamedResource;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface CompressionInformation extends NamedResource {
+ /**
+ * Delayed compression is an Open-SSH specific feature which
+ * informs both the client and server to not compress data before
+ * the session has been authenticated.
+ *
+ * @return if the compression is delayed after authentication or not
+ */
+ boolean isDelayed();
+
+ /**
+ * @return {@code true} if there is any compression executed by
+ * this "compressor" - special case for 'none'
+ */
+ boolean isCompressionExecuted();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionNone.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
new file mode 100644
index 0000000..815e8ce
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.compression;
+
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class CompressionNone extends BaseCompression {
+ private Type type;
+ private int level;
+
+ public CompressionNone() {
+ super(BuiltinCompressions.Constants.NONE);
+ }
+
+ @Override
+ public void init(Type type, int level) {
+ this.type = type;
+ this.level = level;
+ }
+
+ @Override
+ public boolean isCompressionExecuted() {
+ return false;
+ }
+
+ @Override
+ public void compress(Buffer buffer) throws IOException {
+ if (!Type.Deflater.equals(type)) {
+ throw new StreamCorruptedException("Not set up for compression: " + type);
+ }
+ }
+
+ @Override
+ public void uncompress(Buffer from, Buffer to) throws IOException {
+ if (!Type.Inflater.equals(type)) {
+ throw new StreamCorruptedException("Not set up for de-compression: " + type);
+ }
+
+ if (from != to) {
+ throw new StreamCorruptedException("Separate de-compression buffers provided");
+ }
+ }
+
+ @Override
+ public boolean isDelayed() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "[" + type + "/" + level + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
new file mode 100644
index 0000000..e365b91
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.compression;
+
+import java.io.IOException;
+import java.util.zip.DataFormatException;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * ZLib based Compression.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class CompressionZlib extends BaseCompression {
+
+ private static final int BUF_SIZE = 4096;
+
+ private byte[] tmpbuf = new byte[BUF_SIZE];
+ private Deflater compresser;
+ private Inflater decompresser;
+
+ /**
+ * Create a new instance of a ZLib base compression
+ */
+ public CompressionZlib() {
+ this(BuiltinCompressions.Constants.ZLIB);
+ }
+
+ protected CompressionZlib(String name) {
+ super(name);
+ }
+
+ @Override
+ public boolean isDelayed() {
+ return false;
+ }
+
+ @Override
+ public void init(Type type, int level) {
+ compresser = new Deflater(level);
+ decompresser = new Inflater();
+ }
+
+ @Override
+ public void compress(Buffer buffer) throws IOException {
+ compresser.setInput(buffer.array(), buffer.rpos(), buffer.available());
+ buffer.wpos(buffer.rpos());
+ for (int len = compresser.deflate(tmpbuf, 0, tmpbuf.length, Deflater.SYNC_FLUSH);
+ len > 0;
+ len = compresser.deflate(tmpbuf, 0, tmpbuf.length, Deflater.SYNC_FLUSH)) {
+ buffer.putRawBytes(tmpbuf, 0, len);
+ }
+ }
+
+ @Override
+ public void uncompress(Buffer from, Buffer to) throws IOException {
+ decompresser.setInput(from.array(), from.rpos(), from.available());
+ try {
+ for (int len = decompresser.inflate(tmpbuf); len > 0; len = decompresser.inflate(tmpbuf)) {
+ to.putRawBytes(tmpbuf, 0, len);
+ }
+ } catch (DataFormatException e) {
+ throw new IOException("Error decompressing data", e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/compression/package.html
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/compression/package.html b/sshd-common/src/main/java/org/apache/sshd/common/compression/package.html
new file mode 100644
index 0000000..9bd4652
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/compression/package.html
@@ -0,0 +1,25 @@
+<!--
+ 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.
+-->
+<html>
+<head>
+</head>
+<body>
+
+<a href="{@docRoot}/org/apache/sshd/common/compression/Compression.html"><code>Compression</code></a> implementations.
+
+</body>
+</html>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java b/sshd-common/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
new file mode 100644
index 0000000..e153adc
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
@@ -0,0 +1,94 @@
+/*
+ * 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.compression.BuiltinCompressions;
+import org.apache.sshd.common.compression.Compression;
+import org.apache.sshd.common.compression.CompressionFactory;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Provides a "bridge" between the configuration values and the
+ * actual {@link org.apache.sshd.common.NamedFactory} for the {@link Compression}.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum CompressionConfigValue implements CompressionFactory {
+ YES(BuiltinCompressions.zlib),
+ NO(BuiltinCompressions.none),
+ DELAYED(BuiltinCompressions.delayedZlib);
+
+ public static final Set<CompressionConfigValue> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(CompressionConfigValue.class));
+
+ private final CompressionFactory factory;
+
+ CompressionConfigValue(CompressionFactory delegate) {
+ factory = delegate;
+ }
+
+ @Override
+ public final String getName() {
+ return factory.getName();
+ }
+
+ @Override
+ public final Compression create() {
+ return factory.create();
+ }
+
+ @Override
+ public boolean isSupported() {
+ return factory.isSupported();
+ }
+
+ @Override
+ public final String toString() {
+ return getName();
+ }
+
+ @Override
+ public boolean isDelayed() {
+ return factory.isDelayed();
+ }
+
+ @Override
+ public boolean isCompressionExecuted() {
+ return factory.isCompressionExecuted();
+ }
+
+ public static CompressionConfigValue fromName(String n) {
+ if (GenericUtils.isEmpty(n)) {
+ return null;
+ }
+
+ for (CompressionConfigValue v : VALUES) {
+ if (n.equalsIgnoreCase(v.name())) {
+ return v;
+ }
+ }
+
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java b/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
new file mode 100644
index 0000000..10b131c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
@@ -0,0 +1,230 @@
+/*
+ * 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.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.Properties;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+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;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <a href="https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5">ssh_config(5)</a>
+ */
+public final class ConfigFileReaderSupport {
+
+ 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,umac-64@openssh.com,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 ConfigFileReaderSupport() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ 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";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java b/sshd-common/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java
new file mode 100644
index 0000000..7a1cee1
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java
@@ -0,0 +1,51 @@
+/*
+ * 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.List;
+
+import org.apache.sshd.common.Factory;
+
+/**
+ * @param <T> Result type
+ * @param <F> Factory type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class FactoriesListParseResult<T, F extends Factory<T>> extends ListParseResult<F> {
+ protected FactoriesListParseResult(List<F> parsed, List<String> unsupported) {
+ super(parsed, unsupported);
+ }
+
+ /**
+ * @return The {@link List} of successfully parsed {@link Factory} instances
+ * in the <U>same order</U> as they were encountered during parsing
+ */
+ public final List<F> getParsedFactories() {
+ return getParsedValues();
+ }
+
+ /**
+ * @return A {@link List} of unknown/unsupported configuration values for
+ * the factories
+ */
+ public List<String> getUnsupportedFactories() {
+ return getUnsupportedValues();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/ListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/ListParseResult.java b/sshd-common/src/main/java/org/apache/sshd/common/config/ListParseResult.java
new file mode 100644
index 0000000..97590cb
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/ListParseResult.java
@@ -0,0 +1,66 @@
+/*
+ * 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.List;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Used to hold the result of parsing a list of value. Such result contains known
+ * and unknown values - which are accessible via the respective {@link #getParsedValues()}
+ * and {@link #getUnsupportedValues()} methods. <B>Note:</B> the returned {@link List}s may
+ * be un-modifiable, so it is recommended to avoid attempting changing the, returned
+ * list(s)
+ *
+ * @param <E> Type of list item
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class ListParseResult<E> {
+ private final List<E> parsed;
+ private final List<String> unsupported;
+
+ protected ListParseResult(List<E> parsed, List<String> unsupported) {
+ this.parsed = parsed;
+ this.unsupported = unsupported;
+ }
+
+ /**
+ * @return The {@link List} of successfully parsed value instances
+ * in the <U>same order</U> as they were encountered during parsing
+ */
+ public final List<E> getParsedValues() {
+ return parsed;
+ }
+
+ /**
+ * @return A {@link List} of unknown/unsupported configuration values for
+ * the factories
+ */
+ public List<String> getUnsupportedValues() {
+ return unsupported;
+ }
+
+ @Override
+ public String toString() {
+ return "parsed=" + GenericUtils.join(getParsedValues(), ',')
+ + ";unsupported=" + GenericUtils.join(getUnsupportedValues(), ',');
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/LogLevelValue.java b/sshd-common/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
new file mode 100644
index 0000000..2fbdc80
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
@@ -0,0 +1,56 @@
+/*
+ * 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:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html"><I>LogLevel</I> configuration value</A>
+ */
+public enum LogLevelValue {
+ /*
+ * NOTE(s):
+ * 1. DEBUG and DEBUG1 are EQUIVALENT
+ * 2. Order is important (!!!)
+ */
+ QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, DEBUG3;
+
+ public static final Set<LogLevelValue> VALUES =
+ Collections.unmodifiableSet(EnumSet.allOf(LogLevelValue.class));
+
+ public static LogLevelValue fromName(String n) {
+ if (GenericUtils.isEmpty(n)) {
+ return null;
+ }
+
+ for (LogLevelValue l : VALUES) {
+ if (n.equalsIgnoreCase(l.name())) {
+ return l;
+ }
+ }
+
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java b/sshd-common/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java
new file mode 100644
index 0000000..246cae0
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java
@@ -0,0 +1,47 @@
+/*
+ * 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.List;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Holds the result of parsing a list of {@link NamedFactory}ies
+ *
+ * @param <T> Result type
+ * @param <F> Factory type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class NamedFactoriesListParseResult<T, F extends NamedFactory<T>>
+ extends FactoriesListParseResult<T, F> {
+
+ protected NamedFactoriesListParseResult(List<F> parsed, List<String> unsupported) {
+ super(parsed, unsupported);
+ }
+
+ @Override
+ public String toString() {
+ return "parsed=" + NamedResource.getNames(getParsedFactories())
+ + ";unknown=" + GenericUtils.join(getUnsupportedFactories(), ',');
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java b/sshd-common/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java
new file mode 100644
index 0000000..feb45f4
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java
@@ -0,0 +1,57 @@
+/*
+ * 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.List;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @param <R> Type of result {@link NamedResource}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class NamedResourceListParseResult<R extends NamedResource> extends ListParseResult<R> {
+ protected NamedResourceListParseResult(List<R> parsed, List<String> unsupported) {
+ super(parsed, unsupported);
+ }
+
+ /**
+ * @return The {@link List} of successfully parsed {@link NamedResource} instances
+ * in the <U>same order</U> as they were encountered during parsing
+ */
+ public final List<R> getParsedResources() {
+ return getParsedValues();
+ }
+
+ /**
+ * @return A {@link List} of unknown/unsupported configuration values for
+ * the resources
+ */
+ public List<String> getUnsupportedResources() {
+ return getUnsupportedValues();
+ }
+
+ @Override
+ public String toString() {
+ return "parsed=" + NamedResource.getNames(getParsedResources())
+ + ";unknown=" + GenericUtils.join(getUnsupportedResources(), ',');
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java b/sshd-common/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java
new file mode 100644
index 0000000..f52ae36
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/SyslogFacilityValue.java
@@ -0,0 +1,51 @@
+/*
+ * 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:dev@mina.apache.org">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-common/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java b/sshd-common/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java
new file mode 100644
index 0000000..222cf84
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/TimeValueConfig.java
@@ -0,0 +1,180 @@
+/*
+ * 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:dev@mina.apache.org">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;
+ }
+}
[29/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java
new file mode 100644
index 0000000..c2ce550
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrarTest.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.security.eddsa;
+
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.Provider;
+import java.security.Signature;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+
+import org.apache.sshd.common.util.security.SecurityProviderRegistrar;
+import org.apache.sshd.common.util.security.SecurityProviderRegistrarTestSupport;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+import net.i2p.crypto.eddsa.EdDSASecurityProvider;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class EdDSASecurityProviderRegistrarTest extends SecurityProviderRegistrarTestSupport {
+ private static SecurityProviderRegistrar registrarInstance;
+
+ public EdDSASecurityProviderRegistrarTest() {
+ super();
+ }
+
+ @BeforeClass
+ public static void checkEDDSASupported() {
+ Assume.assumeTrue(SecurityUtils.isEDDSACurveSupported());
+ registrarInstance = new EdDSASecurityProviderRegistrar();
+ }
+
+ @Test
+ public void testSupportedSecurityEntities() {
+ assertSecurityEntitySupportState(getCurrentTestName(), registrarInstance, true, registrarInstance.getName(),
+ KeyPairGenerator.class, KeyFactory.class);
+ assertSecurityEntitySupportState(getCurrentTestName(), registrarInstance, true,
+ SecurityUtils.CURVE_ED25519_SHA512, Signature.class);
+
+ Collection<Class<?>> supported = new HashSet<>(Arrays.asList(KeyPairGenerator.class, KeyFactory.class, Signature.class));
+ for (Class<?> entity : SecurityProviderRegistrar.SECURITY_ENTITIES) {
+ if (supported.contains(entity)) {
+ continue;
+ }
+ assertFalse("Unexpected support for " + entity.getSimpleName(), registrarInstance.isSecurityEntitySupported(entity, registrarInstance.getName()));
+ }
+ }
+
+ @Test
+ public void testGetSecurityProvider() {
+ Provider expected = registrarInstance.getSecurityProvider();
+ assertNotNull("No provider created", expected);
+ assertEquals("Mismatched provider name", registrarInstance.getName(), expected.getName());
+ assertObjectInstanceOf("Mismatched provider type", EdDSASecurityProvider.class, expected);
+ }
+
+ @Test
+ public void testGetSecurityProviderCaching() {
+ testGetSecurityProviderCaching(getCurrentTestName(), registrarInstance);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
new file mode 100644
index 0000000..2978a18
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProviderTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.server.keyprovider;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class AbstractGeneratorHostKeyProviderTest extends JUnitTestSupport {
+ public AbstractGeneratorHostKeyProviderTest() {
+ super();
+ }
+
+ @SuppressWarnings("synthetic-access")
+ @Test
+ public void testOverwriteKey() throws Exception {
+ Path tempDir = assertHierarchyTargetFolderExists(getTempTargetFolder());
+ Path keyPairFile = tempDir.resolve(getCurrentTestName() + ".key");
+ Files.deleteIfExists(keyPairFile);
+
+ TestProvider provider = new TestProvider(keyPairFile);
+ provider.loadKeys();
+ assertEquals("Mismatched generate write count", 1, provider.getWriteCount());
+
+ provider = new TestProvider(keyPairFile);
+ provider.setOverwriteAllowed(false);
+ provider.loadKeys();
+ assertEquals("Mismatched load write count", 0, provider.getWriteCount());
+ }
+
+ private static final class TestProvider extends AbstractGeneratorHostKeyProvider {
+ private final AtomicInteger writes = new AtomicInteger(0);
+
+ private TestProvider(Path file) {
+ setKeySize(512);
+ setPath(file);
+ }
+
+ @Override
+ protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException {
+ return null;
+ }
+
+ @Override
+ protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
+ writes.incrementAndGet();
+ }
+
+ public int getWriteCount() {
+ return writes.get();
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
new file mode 100644
index 0000000..3be5b6d
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProviderTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.server.keyprovider;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class PEMGeneratorHostKeyProviderTest extends JUnitTestSupport {
+ public PEMGeneratorHostKeyProviderTest() {
+ super();
+ }
+
+ @Test
+ public void testDSA() throws IOException {
+ Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+ testPEMGeneratorHostKeyProvider(KeyUtils.DSS_ALGORITHM, KeyPairProvider.SSH_DSS, 512, null);
+ }
+
+ @Test
+ public void testRSA() throws IOException {
+ Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+ testPEMGeneratorHostKeyProvider(KeyUtils.RSA_ALGORITHM, KeyPairProvider.SSH_RSA, 512, null);
+ }
+
+ @Test
+ public void testECnistp256() throws IOException {
+ Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+ Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
+ Assume.assumeTrue(ECCurves.nistp256 + " N/A", ECCurves.nistp256.isSupported());
+ testPEMGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP256, -1, new ECGenParameterSpec("prime256v1"));
+ }
+
+ @Test
+ public void testECnistp384() throws IOException {
+ Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+ Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
+ Assume.assumeTrue(ECCurves.nistp384 + " N/A", ECCurves.nistp384.isSupported());
+ testPEMGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP384, -1, new ECGenParameterSpec("P-384"));
+ }
+
+ @Test
+ public void testECnistp521() throws IOException {
+ Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+ Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
+ Assume.assumeTrue(ECCurves.nistp521 + " N/A", ECCurves.nistp521.isSupported());
+ testPEMGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP521, -1, new ECGenParameterSpec("P-521"));
+ }
+
+ private Path testPEMGeneratorHostKeyProvider(String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) throws IOException {
+ Path path = initKeyFileLocation(algorithm);
+ KeyPair kpWrite = invokePEMGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
+ assertTrue("Key file not generated: " + path, Files.exists(path, IoUtils.EMPTY_LINK_OPTIONS));
+
+ KeyPair kpRead = invokePEMGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
+ PublicKey pubWrite = kpWrite.getPublic();
+ PublicKey pubRead = kpRead.getPublic();
+ if (pubWrite instanceof ECPublicKey) {
+ // The algorithm is reported as ECDSA instead of EC
+ assertECPublicKeyEquals("Mismatched EC public key", ECPublicKey.class.cast(pubWrite), ECPublicKey.class.cast(pubRead));
+ } else {
+ assertKeyEquals("Mismatched public keys", pubWrite, pubRead);
+ }
+ return path;
+ }
+
+ private static KeyPair invokePEMGeneratorHostKeyProvider(Path path, String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) {
+ AbstractGeneratorHostKeyProvider provider = SecurityUtils.createGeneratorHostKeyProvider(path.toAbsolutePath().normalize());
+ provider.setAlgorithm(algorithm);
+ provider.setOverwriteAllowed(true);
+ if (keySize > 0) {
+ provider.setKeySize(keySize);
+ }
+ if (keySpec != null) {
+ provider.setKeySpec(keySpec);
+ }
+
+ return validateKeyPairProvider(provider, keyType);
+ }
+
+ private static KeyPair validateKeyPairProvider(KeyPairProvider provider, String keyType) {
+ Iterable<String> types = provider.getKeyTypes();
+ KeyPair kp = null;
+ for (String type : types) {
+ if (keyType.equals(type)) {
+ kp = provider.loadKey(keyType);
+ assertNotNull("Failed to load key for " + keyType, kp);
+ break;
+ }
+ }
+
+ assertNotNull("Expected key type not found: " + keyType, kp);
+ return kp;
+ }
+
+ private Path initKeyFileLocation(String algorithm) throws IOException {
+ Path path = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
+ path = path.resolve(algorithm + "-PEM.key");
+ Files.deleteIfExists(path);
+ return path;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
new file mode 100644
index 0000000..dc5e9fd
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProviderTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.server.keyprovider;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class SimpleGeneratorHostKeyProviderTest extends JUnitTestSupport {
+ public SimpleGeneratorHostKeyProviderTest() {
+ super();
+ }
+
+ @Test
+ public void testDSA() throws IOException {
+ testSimpleGeneratorHostKeyProvider(KeyUtils.DSS_ALGORITHM, KeyPairProvider.SSH_DSS, 512, null);
+ }
+
+ @Test
+ public void testRSA() throws IOException {
+ testSimpleGeneratorHostKeyProvider(KeyUtils.RSA_ALGORITHM, KeyPairProvider.SSH_RSA, 512, null);
+ }
+
+ @Test
+ public void testECnistp256() throws IOException {
+ Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+ Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
+ Assume.assumeTrue(ECCurves.nistp256 + " N/A", ECCurves.nistp256.isSupported());
+ testSimpleGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP256, -1, new ECGenParameterSpec("prime256v1"));
+ }
+
+ @Test
+ public void testECnistp384() throws IOException {
+ Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+ Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
+ Assume.assumeTrue(ECCurves.nistp384 + " N/A", ECCurves.nistp384.isSupported());
+ testSimpleGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP384, -1, new ECGenParameterSpec("P-384"));
+ }
+
+ @Test
+ public void testECnistp521() throws IOException {
+ Assume.assumeTrue("BouncyCastle not registered", SecurityUtils.isBouncyCastleRegistered());
+ Assume.assumeTrue("ECC not supported", SecurityUtils.isECCSupported());
+ Assume.assumeTrue(ECCurves.nistp521 + " N/A", ECCurves.nistp521.isSupported());
+ testSimpleGeneratorHostKeyProvider(KeyUtils.EC_ALGORITHM, KeyPairProvider.ECDSA_SHA2_NISTP521, -1, new ECGenParameterSpec("P-521"));
+ }
+
+ private Path testSimpleGeneratorHostKeyProvider(String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) throws IOException {
+ Path path = initKeyFileLocation(algorithm);
+ KeyPair kpWrite = invokeSimpleGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
+ assertTrue("Key file not generated: " + path, Files.exists(path, IoUtils.EMPTY_LINK_OPTIONS));
+
+ KeyPair kpRead = invokeSimpleGeneratorHostKeyProvider(path, algorithm, keyType, keySize, keySpec);
+ assertKeyPairEquals("Mismatched write/read key pairs", kpWrite, kpRead);
+ return path;
+ }
+
+ private static KeyPair invokeSimpleGeneratorHostKeyProvider(Path path, String algorithm, String keyType, int keySize, AlgorithmParameterSpec keySpec) {
+ SimpleGeneratorHostKeyProvider provider = new SimpleGeneratorHostKeyProvider();
+ provider.setAlgorithm(algorithm);
+ provider.setOverwriteAllowed(true);
+ provider.setPath(path);
+ if (keySize > 0) {
+ provider.setKeySize(keySize);
+ }
+ if (keySpec != null) {
+ provider.setKeySpec(keySpec);
+ }
+
+ return validateKeyPairProvider(provider, keyType);
+ }
+
+ private static KeyPair validateKeyPairProvider(KeyPairProvider provider, String keyType) {
+ Iterable<String> types = provider.getKeyTypes();
+ KeyPair kp = null;
+ for (String type : types) {
+ if (keyType.equals(type)) {
+ kp = provider.loadKey(keyType);
+ assertNotNull("Failed to load key for " + keyType, kp);
+ break;
+ }
+ }
+
+ assertNotNull("Expected key type not found: " + keyType, kp);
+ return kp;
+ }
+
+ private Path initKeyFileLocation(String algorithm) throws IOException {
+ Path path = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
+ path = path.resolve(algorithm + "-simple.key");
+ Files.deleteIfExists(path);
+ return path;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java b/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java
new file mode 100644
index 0000000..7ae3703
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/CommonTestSupportUtils.java
@@ -0,0 +1,620 @@
+/*
+ * 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.util.test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.security.CodeSource;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.ProtectionDomain;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.keyprovider.KeyPairProviderHolder;
+import org.apache.sshd.common.random.Random;
+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.security.SecurityUtils;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class CommonTestSupportUtils {
+ /**
+ * URL/URI scheme that refers to a file
+ */
+ public static final String FILE_URL_SCHEME = "file";
+ /**
+ * Prefix used in URL(s) that reference a file resource
+ */
+ public static final String FILE_URL_PREFIX = FILE_URL_SCHEME + ":";
+
+ /**
+ * Separator used in URL(s) that reference a resource inside a JAR
+ * to denote the sub-path inside the JAR
+ */
+ public static final char RESOURCE_SUBPATH_SEPARATOR = '!';
+
+ /**
+ * Suffix of JAR files
+ */
+ public static final String JAR_FILE_SUFFIX = ".jar";
+
+ /**
+ * URL/URI scheme that refers to a JAR
+ */
+ public static final String JAR_URL_SCHEME = "jar";
+
+ /**
+ * Prefix used in URL(s) that reference a resource inside a JAR
+ */
+ public static final String JAR_URL_PREFIX = JAR_URL_SCHEME + ":";
+
+ /**
+ * Suffix of compile Java class files
+ */
+ public static final String CLASS_FILE_SUFFIX = ".class";
+
+ public static final List<String> TARGET_FOLDER_NAMES = // NOTE: order is important
+ Collections.unmodifiableList(
+ Arrays.asList("target" /* Maven */, "build" /* Gradle */));
+
+ public static final String DEFAULT_TEST_HOST_KEY_PROVIDER_ALGORITHM = KeyUtils.RSA_ALGORITHM;
+ // uses a cached instance to avoid re-creating the keys as it is a time-consuming effort
+ private static final AtomicReference<KeyPairProvider> KEYPAIR_PROVIDER_HOLDER = new AtomicReference<>();
+ // uses a cached instance to avoid re-creating the keys as it is a time-consuming effort
+ private static final Map<String, FileKeyPairProvider> PROVIDERS_MAP = new ConcurrentHashMap<>();
+
+ private CommonTestSupportUtils() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+
+ /**
+ * @param clazz A {@link Class} object
+ * @return A {@link URI} to the location of the class bytes container
+ * - e.g., the root folder, the containing JAR, etc.. Returns
+ * {@code null} if location could not be resolved
+ * @throws URISyntaxException if location is not a valid URI
+ * @see #getClassContainerLocationURL(Class)
+ */
+ public static URI getClassContainerLocationURI(Class<?> clazz) throws URISyntaxException {
+ URL url = getClassContainerLocationURL(clazz);
+ return (url == null) ? null : url.toURI();
+ }
+
+ /**
+ * @param clazz A {@link Class} object
+ * @return A {@link URL} to the location of the class bytes container
+ * - e.g., the root folder, the containing JAR, etc.. Returns
+ * {@code null} if location could not be resolved
+ */
+ public static URL getClassContainerLocationURL(Class<?> clazz) {
+ ProtectionDomain pd = clazz.getProtectionDomain();
+ CodeSource cs = (pd == null) ? null : pd.getCodeSource();
+ URL url = (cs == null) ? null : cs.getLocation();
+ if (url == null) {
+ url = getClassBytesURL(clazz);
+ if (url == null) {
+ return null;
+ }
+
+ String srcForm = getURLSource(url);
+ if (GenericUtils.isEmpty(srcForm)) {
+ return null;
+ }
+
+ try {
+ url = new URL(srcForm);
+ } catch (MalformedURLException e) {
+ throw new IllegalArgumentException("getClassContainerLocationURL(" + clazz.getName() + ")"
+ + " Failed to create URL=" + srcForm + " from " + url.toExternalForm()
+ + ": " + e.getMessage());
+ }
+ }
+
+ return url;
+ }
+
+ /**
+ * @param uri The {@link URI} value - ignored if {@code null}
+ * @return The URI(s) source path where {@link #JAR_URL_PREFIX} and
+ * any sub-resource are stripped
+ * @see #getURLSource(String)
+ */
+ public static String getURLSource(URI uri) {
+ return getURLSource((uri == null) ? null : uri.toString());
+ }
+
+ /**
+ * @param url The {@link URL} value - ignored if {@code null}
+ * @return The URL(s) source path where {@link #JAR_URL_PREFIX} and
+ * any sub-resource are stripped
+ * @see #getURLSource(String)
+ */
+ public static String getURLSource(URL url) {
+ return getURLSource((url == null) ? null : url.toExternalForm());
+ }
+
+ /**
+ * @param externalForm The {@link URL#toExternalForm()} string - ignored if
+ * {@code null}/empty
+ * @return The URL(s) source path where {@link #JAR_URL_PREFIX} and
+ * any sub-resource are stripped
+ */
+ public static String getURLSource(String externalForm) {
+ String url = externalForm;
+ if (GenericUtils.isEmpty(url)) {
+ return url;
+ }
+
+ url = stripJarURLPrefix(externalForm);
+ if (GenericUtils.isEmpty(url)) {
+ return url;
+ }
+
+ int sepPos = url.indexOf(RESOURCE_SUBPATH_SEPARATOR);
+ if (sepPos < 0) {
+ return adjustURLPathValue(url);
+ } else {
+ return adjustURLPathValue(url.substring(0, sepPos));
+ }
+ }
+
+ /**
+ * @param url A {@link URL} - ignored if {@code null}
+ * @return The path after stripping any trailing '/' provided the path
+ * is not '/' itself
+ * @see #adjustURLPathValue(String)
+ */
+ public static String adjustURLPathValue(URL url) {
+ return adjustURLPathValue((url == null) ? null : url.getPath());
+ }
+
+ /**
+ * @param path A URL path value - ignored if {@code null}/empty
+ * @return The path after stripping any trailing '/' provided the path
+ * is not '/' itself
+ */
+ public static String adjustURLPathValue(final String path) {
+ final int pathLen = (path == null) ? 0 : path.length();
+ if ((pathLen <= 1) || (path.charAt(pathLen - 1) != '/')) {
+ return path;
+ }
+
+ return path.substring(0, pathLen - 1);
+ }
+
+ public static String stripJarURLPrefix(String externalForm) {
+ String url = externalForm;
+ if (GenericUtils.isEmpty(url)) {
+ return url;
+ }
+
+ if (url.startsWith(JAR_URL_PREFIX)) {
+ return url.substring(JAR_URL_PREFIX.length());
+ }
+
+ return url;
+ }
+
+ /**
+ * @param clazz The request {@link Class}
+ * @return A {@link URL} to the location of the <code>.class</code> file
+ * - {@code null} if location could not be resolved
+ */
+ public static URL getClassBytesURL(Class<?> clazz) {
+ String className = clazz.getName();
+ int sepPos = className.indexOf('$');
+ // if this is an internal class, then need to use its parent as well
+ if (sepPos > 0) {
+ sepPos = className.lastIndexOf('.');
+ if (sepPos > 0) {
+ className = className.substring(sepPos + 1);
+ }
+ } else {
+ className = clazz.getSimpleName();
+ }
+
+ return clazz.getResource(className + CLASS_FILE_SUFFIX);
+ }
+
+ public static String getClassBytesResourceName(Class<?> clazz) {
+ return getClassBytesResourceName((clazz == null) ? null : clazz.getName());
+ }
+
+ /**
+ * @param name The fully qualified class name - ignored if {@code null}/empty
+ * @return The relative path of the class file byte-code resource
+ */
+ public static String getClassBytesResourceName(String name) {
+ if (GenericUtils.isEmpty(name)) {
+ return name;
+ } else {
+ return name.replace('.', '/') + CLASS_FILE_SUFFIX;
+ }
+ }
+
+ public static Path resolve(Path root, String... children) {
+ if (GenericUtils.isEmpty(children)) {
+ return root;
+ } else {
+ return resolve(root, Arrays.asList(children));
+ }
+ }
+
+ public static Path resolve(Path root, Collection<String> children) {
+ Path path = root;
+ if (!GenericUtils.isEmpty(children)) {
+ for (String child : children) {
+ path = path.resolve(child);
+ }
+ }
+
+ return path;
+ }
+
+ /**
+ * @param anchor An anchor {@link Class} whose container we want to use
+ * as the starting point for the "target" folder lookup up the
+ * hierarchy
+ * @return The "target" <U>folder</U> - {@code null} if not found
+ * @see #detectTargetFolder(File)
+ */
+ public static File detectTargetFolder(Class<?> anchor) {
+ return detectTargetFolder(getClassContainerLocationFile(anchor));
+ }
+
+ /**
+ * @param clazz A {@link Class} object
+ * @return A {@link File} of the location of the class bytes container
+ * - e.g., the root folder, the containing JAR, etc.. Returns
+ * {@code null} if location could not be resolved
+ * @throws IllegalArgumentException If location is not a valid {@link File} location
+ * @see #getClassContainerLocationURI(Class)
+ * @see #toFileSource(URI)
+ */
+ public static File getClassContainerLocationFile(Class<?> clazz)
+ throws IllegalArgumentException {
+ try {
+ URI uri = getClassContainerLocationURI(clazz);
+ return toFileSource(uri);
+ } catch (URISyntaxException | MalformedURLException e) {
+ throw new IllegalArgumentException(e.getClass().getSimpleName() + ": " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Converts a {@link URL} that may refer to an internal resource to
+ * a {@link File} representing is "source" container (e.g.,
+ * if it is a resource in a JAR, then the result is the JAR's path)
+ *
+ * @param url The {@link URL} - ignored if {@code null}
+ * @return The matching {@link File}
+ * @throws MalformedURLException If source URL does not refer to a file location
+ * @see #toFileSource(URI)
+ */
+ public static File toFileSource(URL url) throws MalformedURLException {
+ if (url == null) {
+ return null;
+ }
+
+ try {
+ return toFileSource(url.toURI());
+ } catch (URISyntaxException e) {
+ throw new MalformedURLException("toFileSource(" + url.toExternalForm() + ")"
+ + " cannot (" + e.getClass().getSimpleName() + ")"
+ + " convert to URI: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Converts a {@link URI} that may refer to an internal resource to
+ * a {@link File} representing is "source" container (e.g.,
+ * if it is a resource in a JAR, then the result is the JAR's path)
+ *
+ * @param uri The {@link URI} - ignored if {@code null}
+ * @return The matching {@link File}
+ * @throws MalformedURLException If source URI does not refer to a file location
+ * @see #getURLSource(URI)
+ */
+ public static File toFileSource(URI uri) throws MalformedURLException {
+ String src = getURLSource(uri);
+ if (GenericUtils.isEmpty(src)) {
+ return null;
+ }
+
+ if (!src.startsWith(FILE_URL_PREFIX)) {
+ throw new MalformedURLException("toFileSource(" + src + ") not a '" + FILE_URL_SCHEME + "' scheme");
+ }
+
+ try {
+ return new File(new URI(src));
+ } catch (URISyntaxException e) {
+ throw new MalformedURLException("toFileSource(" + src + ")"
+ + " cannot (" + e.getClass().getSimpleName() + ")"
+ + " convert to URI: " + e.getMessage());
+ }
+ }
+
+ /**
+ * @param anchorFile An anchor {@link File} we want to use
+ * as the starting point for the "target" or "build" folder
+ * lookup up the hierarchy
+ * @return The "target" <U>folder</U> - {@code null} if not found
+ */
+ public static File detectTargetFolder(File anchorFile) {
+ for (File file = anchorFile; file != null; file = file.getParentFile()) {
+ if (!file.isDirectory()) {
+ continue;
+ }
+
+ String name = file.getName();
+ if (TARGET_FOLDER_NAMES.contains(name)) {
+ return file;
+ }
+ }
+
+ return null;
+ }
+
+ public static KeyPair generateKeyPair(String algorithm, int keySize) throws GeneralSecurityException {
+ KeyPairGenerator gen = SecurityUtils.getKeyPairGenerator(algorithm);
+ if (KeyUtils.EC_ALGORITHM.equalsIgnoreCase(algorithm)) {
+ ECCurves curve = ECCurves.fromCurveSize(keySize);
+ if (curve == null) {
+ throw new InvalidKeySpecException("Unknown curve for key size=" + keySize);
+ }
+ gen.initialize(curve.getParameters());
+ } else {
+ gen.initialize(keySize);
+ }
+
+ return gen.generateKeyPair();
+ }
+
+ public static KeyPairProvider createTestHostKeyProvider(Class<?> anchor) {
+ KeyPairProvider provider = KEYPAIR_PROVIDER_HOLDER.get();
+ if (provider != null) {
+ return provider;
+ }
+
+ File targetFolder = Objects.requireNonNull(CommonTestSupportUtils.detectTargetFolder(anchor), "Failed to detect target folder");
+ File file = new File(targetFolder, "hostkey." + DEFAULT_TEST_HOST_KEY_PROVIDER_ALGORITHM.toLowerCase());
+ provider = createTestHostKeyProvider(file);
+
+ KeyPairProvider prev = KEYPAIR_PROVIDER_HOLDER.getAndSet(provider);
+ if (prev != null) { // check if somebody else beat us to it
+ return prev;
+ } else {
+ return provider;
+ }
+ }
+
+ public static KeyPairProvider createTestHostKeyProvider(File file) {
+ return createTestHostKeyProvider(Objects.requireNonNull(file, "No file").toPath());
+ }
+
+ public static KeyPairProvider createTestHostKeyProvider(Path path) {
+ SimpleGeneratorHostKeyProvider keyProvider = new SimpleGeneratorHostKeyProvider();
+ keyProvider.setPath(Objects.requireNonNull(path, "No path"));
+ keyProvider.setAlgorithm(DEFAULT_TEST_HOST_KEY_PROVIDER_ALGORITHM);
+ return validateKeyPairProvider(keyProvider);
+ }
+
+ public static KeyPair getFirstKeyPair(KeyPairProviderHolder holder) {
+ return getFirstKeyPair(Objects.requireNonNull(holder, "No holder").getKeyPairProvider());
+ }
+
+ public static KeyPair getFirstKeyPair(KeyIdentityProvider provider) {
+ Objects.requireNonNull(provider, "No key pair provider");
+ Iterable<? extends KeyPair> pairs = Objects.requireNonNull(provider.loadKeys(), "No loaded keys");
+ Iterator<? extends KeyPair> iter = Objects.requireNonNull(pairs.iterator(), "No keys iterator");
+ ValidateUtils.checkTrue(iter.hasNext(), "Empty loaded kyes iterator");
+ return Objects.requireNonNull(iter.next(), "No key pair in iterator");
+ }
+
+ private static File getFile(String resource) {
+ URL url = CommonTestSupportUtils.class.getClassLoader().getResource(resource);
+ try {
+ return new File(url.toURI());
+ } catch (URISyntaxException e) {
+ return new File(url.getPath());
+ }
+ }
+
+ public static File resolve(File root, String... children) {
+ if (GenericUtils.isEmpty(children)) {
+ return root;
+ } else {
+ return resolve(root, Arrays.asList(children));
+ }
+ }
+
+ public static File resolve(File root, Collection<String> children) {
+ File path = root;
+ if (!GenericUtils.isEmpty(children)) {
+ for (String child : children) {
+ path = new File(path, child);
+ }
+ }
+
+ return path;
+ }
+
+ /**
+ * Removes the specified file - if it is a directory, then its children
+ * are deleted recursively and then the directory itself. <B>Note:</B>
+ * no attempt is made to make sure that {@link File#delete()} was successful
+ *
+ * @param file The {@link File} to be deleted - ignored if {@code null}
+ * or does not exist anymore
+ * @return The <tt>file</tt> argument
+ */
+ public static File deleteRecursive(File file) {
+ if ((file == null) || (!file.exists())) {
+ return file;
+ }
+
+ if (file.isDirectory()) {
+ File[] children = file.listFiles();
+ if (!GenericUtils.isEmpty(children)) {
+ for (File child : children) {
+ deleteRecursive(child);
+ }
+ }
+ }
+
+ // seems that if a file is not writable it cannot be deleted
+ if (!file.canWrite()) {
+ file.setWritable(true, false);
+ }
+
+ if (!file.delete()) {
+ System.err.append("Failed to delete ").println(file.getAbsolutePath());
+ }
+
+ return file;
+ }
+
+ /**
+ * Removes the specified file - if it is a directory, then its children
+ * are deleted recursively and then the directory itself.
+ *
+ * @param path The file {@link Path} to be deleted - ignored if {@code null}
+ * or does not exist anymore
+ * @param options The {@link LinkOption}s to use
+ * @return The <tt>path</tt> argument
+ * @throws IOException If failed to access/remove some file(s)
+ */
+ public static Path deleteRecursive(Path path, LinkOption... options) throws IOException {
+ if ((path == null) || (!Files.exists(path))) {
+ return path;
+ }
+
+ if (Files.isDirectory(path)) {
+ try (DirectoryStream<Path> ds = Files.newDirectoryStream(path)) {
+ for (Path child : ds) {
+ deleteRecursive(child, options);
+ }
+ }
+ }
+
+ try {
+ // seems that if a file is not writable it cannot be deleted
+ if (!Files.isWritable(path)) {
+ path.toFile().setWritable(true, false);
+ }
+ Files.delete(path);
+ } catch (IOException e) {
+ // same logic as deleteRecursive(File) which does not check if deletion succeeded
+ System.err.append("Failed (").append(e.getClass().getSimpleName()).append(")")
+ .append(" to delete ").append(path.toString())
+ .append(": ").println(e.getMessage());
+ }
+
+ return path;
+ }
+
+ public static String resolveRelativeRemotePath(Path root, Path file) {
+ Path relPath = root.relativize(file);
+ return relPath.toString().replace(File.separatorChar, '/');
+ }
+
+ public static FileKeyPairProvider createTestKeyPairProvider(String resource) {
+ File file = getFile(resource);
+ String filePath = file.getAbsolutePath();
+ FileKeyPairProvider provider = PROVIDERS_MAP.get(filePath);
+ if (provider != null) {
+ return provider;
+ }
+
+ provider = new FileKeyPairProvider();
+ provider.setFiles(Collections.singletonList(file));
+ provider = validateKeyPairProvider(provider);
+
+ FileKeyPairProvider prev = PROVIDERS_MAP.put(filePath, provider);
+ if (prev != null) { // check if somebody else beat us to it
+ return prev;
+ } else {
+ return provider;
+ }
+ }
+
+ private static <P extends KeyIdentityProvider> P validateKeyPairProvider(P provider) {
+ Objects.requireNonNull(provider, "No provider");
+
+ // get the I/O out of the way
+ Iterable<KeyPair> keys = Objects.requireNonNull(provider.loadKeys(), "No keys loaded");
+ if (keys instanceof Collection<?>) {
+ ValidateUtils.checkNotNullAndNotEmpty((Collection<?>) keys, "Empty keys loaded");
+ }
+
+ return provider;
+ }
+
+ public static Random getRandomizerInstance() {
+ Factory<Random> factory = SecurityUtils.getRandomFactory();
+ return factory.create();
+ }
+
+ /**
+ * @param path The {@link Path} to write the data to
+ * @param data The data to write (as UTF-8)
+ * @return The UTF-8 data bytes
+ * @throws IOException If failed to write
+ */
+ public static byte[] writeFile(Path path, String data) throws IOException {
+ try (OutputStream fos = Files.newOutputStream(path, IoUtils.EMPTY_OPEN_OPTIONS)) {
+ byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
+ fos.write(bytes);
+ return bytes;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParameters.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParameters.java b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParameters.java
new file mode 100644
index 0000000..acf44a5
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParameters.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.util.test;
+
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters;
+import org.junit.runners.parameterized.TestWithParameters;
+
+/**
+ * Uses a cached created instance instead of a new one on every call of {@code #createTest()}
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class JUnit4ClassRunnerWithParameters extends BlockJUnit4ClassRunnerWithParameters {
+ private volatile Object testInstance;
+
+ public JUnit4ClassRunnerWithParameters(TestWithParameters test) throws InitializationError {
+ super(test);
+ }
+
+ @Override
+ public Object createTest() throws Exception {
+ synchronized (this) {
+ if (testInstance == null) {
+ testInstance = super.createTest();
+ }
+ }
+
+ return testInstance;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParametersFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParametersFactory.java b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParametersFactory.java
new file mode 100644
index 0000000..12ab252
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParametersFactory.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.util.test;
+
+import org.junit.runner.Runner;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.parameterized.ParametersRunnerFactory;
+import org.junit.runners.parameterized.TestWithParameters;
+
+/**
+ * Avoids re-creating a test class instance for each parameterized test method. Usage:
+ *
+ * <PRE><code>
+ * @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+ * @RunWith(Parameterized.class)
+ * @UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+ * public class MyParameterizedTest {
+ * public MyParameterizedTest(...params...) {
+ * ....
+ * }
+ *
+ * @Parameters(...)
+ * public static List<Object[]> parameters() {
+ * ...
+ * }
+ * }
+ * </code></PRE>
+ *
+ * @see JUnit4ClassRunnerWithParameters
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class JUnit4ClassRunnerWithParametersFactory implements ParametersRunnerFactory {
+ public JUnit4ClassRunnerWithParametersFactory() {
+ super();
+ }
+
+ @Override
+ public Runner createRunnerForTestWithParameters(TestWithParameters test) throws InitializationError {
+ return new JUnit4ClassRunnerWithParameters(test);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4SingleInstanceClassRunner.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4SingleInstanceClassRunner.java b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4SingleInstanceClassRunner.java
new file mode 100644
index 0000000..a8f11ce
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnit4SingleInstanceClassRunner.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.util.test;
+
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.TestClass;
+
+/**
+ * @see <A HREF="https://issues.apache.org/jira/browse/SSHD-764">SSHD-764</A>
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class JUnit4SingleInstanceClassRunner extends BlockJUnit4ClassRunner {
+ private final AtomicReference<Map.Entry<Class<?>, ?>> testHolder = new AtomicReference<>();
+
+ public JUnit4SingleInstanceClassRunner(Class<?> klass) throws InitializationError {
+ super(klass);
+ }
+
+ @Override
+ protected Object createTest() throws Exception {
+ Map.Entry<Class<?>, ?> lastTest = testHolder.get();
+ Class<?> lastTestClass = (lastTest == null) ? null : lastTest.getKey();
+ TestClass curTest = getTestClass();
+ Class<?> curTestClass = curTest.getJavaClass();
+ if (curTestClass == lastTestClass) {
+ return lastTest.getValue();
+ }
+
+ Object instance = super.createTest();
+ testHolder.set(new SimpleImmutableEntry<>(curTestClass, instance));
+ return instance;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java
new file mode 100644
index 0000000..a854699
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/JUnitTestSupport.java
@@ -0,0 +1,572 @@
+/*
+ * 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.util.test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.ECField;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.EllipticCurve;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@RunWith(JUnit4SingleInstanceClassRunner.class)
+public abstract class JUnitTestSupport extends Assert {
+ public static final String TEMP_SUBFOLDER_NAME = "temp";
+ public static final boolean OUTPUT_DEBUG_MESSAGES =
+ Boolean.parseBoolean(System.getProperty("org.apache.sshd.test.outputDebugMessages", "false"));
+ public static final String MAIN_SUBFOLDER = "main";
+ public static final String TEST_SUBFOLDER = "test";
+ public static final String RESOURCES_SUBFOLDER = "resources";
+
+ // useful test sizes for keys
+ public static final List<Integer> DSS_SIZES =
+ Collections.unmodifiableList(Arrays.asList(512, 768, 1024));
+ public static final List<Integer> RSA_SIZES =
+ Collections.unmodifiableList(Arrays.asList(1024, 2048, 3072, 4096));
+ public static final List<Integer> ED25519_SIZES =
+ Collections.unmodifiableList(Arrays.asList(256));
+
+ @Rule
+ public final TestName testNameHolder = new TestName();
+
+ private Path targetFolder;
+ private Path tempFolder;
+
+ protected JUnitTestSupport() {
+ super();
+ }
+
+ public final String getCurrentTestName() {
+ return testNameHolder.getMethodName();
+ }
+
+ /**
+ * Attempts to build a <U>relative</U> path whose root is the location
+ * of the TEMP sub-folder of the Maven "target" folder associated
+ * with the project
+ *
+ * @param comps The path components - ignored if {@code null}/empty
+ * @return The {@link Path} representing the result - same as target folder if no components
+ * @see #TEMP_SUBFOLDER_NAME
+ * @see #getTargetRelativeFile(Collection)
+ */
+ protected Path getTempTargetRelativeFile(String... comps) {
+ return getTempTargetRelativeFile(GenericUtils.isEmpty(comps) ? Collections.emptyList() : Arrays.asList(comps));
+ }
+
+ /**
+ * Attempts to build a <U>relative</U> path whose root is the location
+ * of the TEMP sub-folder of the Maven "target" folder associated
+ * with the project
+ *
+ * @param comps The path components - ignored if {@code null}/empty
+ * @return The {@link Path} representing the result - same as target folder if no components
+ * @see #TEMP_SUBFOLDER_NAME
+ * @see #getTempTargetFolder()
+ */
+ protected Path getTempTargetRelativeFile(Collection<String> comps) {
+ return CommonTestSupportUtils.resolve(getTempTargetFolder(), comps);
+ }
+
+ /**
+ * @return The TEMP sub-folder {@link Path} of the Maven "target" folder
+ * associated with the project - never {@code null}
+ */
+ protected Path getTempTargetFolder() {
+ synchronized (TEMP_SUBFOLDER_NAME) {
+ if (tempFolder == null) {
+ tempFolder = Objects.requireNonNull(detectTargetFolder(), "No target folder detected").resolve(TEMP_SUBFOLDER_NAME);
+ }
+ }
+
+ return tempFolder;
+ }
+
+ /**
+ * Attempts to build a <U>relative</U> path whose root is the location
+ * of the Maven "target" folder associated with the project
+ *
+ * @param comps The path components - ignored if {@code null}/empty
+ * @return The {@link Path} representing the result - same as target folder if no components
+ */
+ protected Path getTargetRelativeFile(String... comps) {
+ return getTargetRelativeFile(GenericUtils.isEmpty(comps) ? Collections.emptyList() : Arrays.asList(comps));
+ }
+
+ /**
+ * Attempts to build a <U>relative</U> path whose root is the location
+ * of the Maven "target" folder associated with the project
+ *
+ * @param comps The path components - ignored if {@code null}/empty
+ * @return The {@link Path} representing the result - same as target folder if no components
+ * @see #detectTargetFolder()
+ */
+ protected Path getTargetRelativeFile(Collection<String> comps) {
+ return CommonTestSupportUtils.resolve(detectTargetFolder(), comps);
+ }
+
+ /**
+ * Attempts to detect the location of the Maven "target" folder
+ * associated with the project that contains the actual class extending this
+ * base class
+ *
+ * @return The {@link File} representing the location of the "target" folder
+ * @throws IllegalArgumentException If failed to detect the folder
+ */
+ protected Path detectTargetFolder() throws IllegalArgumentException {
+ synchronized (TEMP_SUBFOLDER_NAME) {
+ if (targetFolder == null) {
+ File path = CommonTestSupportUtils.detectTargetFolder(getClass());
+ targetFolder = Objects.requireNonNull(path, "Failed to detect target folder").toPath();
+ }
+ }
+
+ return targetFolder;
+ }
+
+ /**
+ * Creates a folder bearing the class's simple name under the project's target temporary folder
+ *
+ * @return The created folder {@link Path}
+ * @throws IOException If failed to detect or create the folder's location
+ * @see #detectTargetFolder() detectTargetFolder
+ * @see #assertHierarchyTargetFolderExists(Path, LinkOption...) assertHierarchyTargetFolderExists
+ */
+ protected Path createTempClassFolder() throws IOException {
+ Path tmpDir = detectTargetFolder();
+ return assertHierarchyTargetFolderExists(tmpDir.resolve(getClass().getSimpleName()));
+ }
+
+ protected Path detectSourcesFolder() throws IllegalStateException {
+ Path target = detectTargetFolder();
+ Path parent = target.getParent();
+ return parent.resolve("src");
+ }
+
+ protected Path getTestResourcesFolder() {
+ try {
+ URL url = getClass().getResource(getClass().getSimpleName() + ".class");
+ return Paths.get(url.toURI()).getParent();
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected Path getClassResourcesFolder(String resType /* test or main */) {
+ return getClassResourcesFolder(resType, getClass());
+ }
+
+ protected Path getClassResourcesFolder(String resType /* test or main */, Class<?> clazz) {
+ return getPackageResourcesFolder(resType, clazz.getPackage());
+ }
+
+ protected Path getPackageResourcesFolder(String resType /* test or main */, Package pkg) {
+ return getPackageResourcesFolder(resType, pkg.getName());
+ }
+
+ protected Path getPackageResourcesFolder(String resType /* test or main */, String pkgName) {
+ Path src = detectSourcesFolder();
+ Path root = src.resolve(resType);
+ Path resources = root.resolve(RESOURCES_SUBFOLDER);
+ return resources.resolve(pkgName.replace('.', File.separatorChar));
+ }
+
+ protected KeyPairProvider createTestHostKeyProvider() {
+ return CommonTestSupportUtils.createTestHostKeyProvider(getClass());
+ }
+
+ /* ------------------- Useful extra test helpers ---------------------- */
+
+ public static String shuffleCase(CharSequence cs) {
+ if (GenericUtils.isEmpty(cs)) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder(cs.length());
+ for (int index = 0; index < cs.length(); index++) {
+ char ch = cs.charAt(index);
+ double v = Math.random();
+ if (Double.compare(v, 0.3d) < 0) {
+ ch = Character.toUpperCase(ch);
+ } else if ((Double.compare(v, 0.3d) >= 0) && (Double.compare(v, 0.6d) < 0)) {
+ ch = Character.toLowerCase(ch);
+ }
+ sb.append(ch);
+ }
+
+ return sb.toString();
+ }
+
+ public static String repeat(CharSequence csq, int nTimes) {
+ if (GenericUtils.isEmpty(csq) || (nTimes <= 0)) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder(nTimes * csq.length());
+ for (int index = 0; index < nTimes; index++) {
+ sb.append(csq);
+ }
+
+ return sb.toString();
+ }
+
+ public static List<Object[]> parameterize(Collection<?> params) {
+ if (GenericUtils.isEmpty(params)) {
+ return Collections.emptyList();
+ }
+
+ List<Object[]> result = new ArrayList<>(params.size());
+ for (Object p : params) {
+ result.add(new Object[]{p});
+ }
+
+ return result;
+ }
+
+ /* ----------------------- Useful extra assertions --------------------- */
+
+ public static void assertEquals(String message, boolean expected, boolean actual) {
+ assertEquals(message, Boolean.valueOf(expected), Boolean.valueOf(actual));
+ }
+
+ public static <T> void assertEquals(String message, Iterable<? extends T> expected, Iterable<? extends T> actual) {
+ if (expected != actual) {
+ assertEquals(message, expected.iterator(), actual.iterator());
+ }
+ }
+
+ public static <T> void assertEquals(String message, Iterator<? extends T> expected, Iterator<? extends T> actual) {
+ if (expected == actual) {
+ return;
+ }
+
+ for (int index = 0; expected.hasNext(); index++) {
+ assertTrue(message + "[next actual index=" + index + "]", actual.hasNext());
+
+ T expValue = expected.next();
+ T actValue = actual.next();
+ assertEquals(message + "[iterator index=" + index + "]", expValue, actValue);
+ }
+
+ // once expected is exhausted make sure no more actual items left
+ assertFalse(message + "[non-empty-actual]", actual.hasNext());
+ }
+
+ public static Path assertHierarchyTargetFolderExists(Path folder, LinkOption... options) throws IOException {
+ if (Files.exists(folder, options)) {
+ assertTrue("Target is an existing file instead of a folder: " + folder, Files.isDirectory(folder, options));
+ } else {
+ Files.createDirectories(folder);
+ }
+
+ return folder;
+ }
+
+ public static void assertFileContentsEquals(String prefix, Path expected, Path actual) throws IOException {
+ long cmpSize = Files.size(expected);
+ assertEquals(prefix + ": Mismatched file size", cmpSize, Files.size(expected));
+
+ try (InputStream expStream = Files.newInputStream(expected);
+ InputStream actStream = Files.newInputStream(actual)) {
+ byte[] expData = new byte[IoUtils.DEFAULT_COPY_SIZE];
+ byte[] actData = new byte[expData.length];
+
+ for (long offset = 0L; offset < cmpSize;) {
+ Arrays.fill(expData, (byte) 0);
+ int expLen = expStream.read(expData);
+ Arrays.fill(actData, (byte) 0);
+ int actLen = actStream.read(actData);
+ assertEquals(prefix + ": Mismatched read size at offset=" + offset, expLen, actLen);
+ assertArrayEquals(prefix + ": Mismatched data at offset=" + offset, expData, actData);
+
+ offset += expLen;
+ }
+ }
+ }
+
+ public static File assertHierarchyTargetFolderExists(File folder) {
+ if (folder.exists()) {
+ assertTrue("Target is an existing file instead of a folder: " + folder.getAbsolutePath(), folder.isDirectory());
+ } else {
+ assertTrue("Failed to create hierarchy of " + folder.getAbsolutePath(), folder.mkdirs());
+ }
+
+ return folder;
+ }
+
+ public static void assertObjectInstanceOf(String message, Class<?> expected, Object obj) {
+ assertNotNull(message + " - no actual object", obj);
+
+ Class<?> actual = obj.getClass();
+ if (!expected.isAssignableFrom(actual)) {
+ fail(message + " - actual object type (" + actual.getName() + ") incompatible with expected (" + expected.getName() + ")");
+ }
+ }
+
+ public static <E> void assertListEquals(String message, List<? extends E> expected, List<? extends E> actual) {
+ int expSize = GenericUtils.size(expected);
+ int actSize = GenericUtils.size(actual);
+ assertEquals(message + "[size]", expSize, actSize);
+
+ for (int index = 0; index < expSize; index++) {
+ E expValue = expected.get(index);
+ E actValue = actual.get(index);
+ assertEquals(message + "[" + index + "]", expValue, actValue);
+ }
+ }
+
+ public static <K, V> void assertMapEquals(String message, Map<? extends K, ? extends V> expected, Map<? super K, ? extends V> actual) {
+ int numItems = GenericUtils.size(expected);
+ assertEquals(message + "[size]", numItems, GenericUtils.size(actual));
+
+ if (numItems > 0) {
+ expected.forEach((key, expValue) -> {
+ V actValue = actual.get(key);
+ assertEquals(message + "[" + key + "]", expValue, actValue);
+ });
+ }
+ }
+
+ public static void assertKeyPairEquals(String message, KeyPair expected, KeyPair actual) {
+ assertKeyEquals(message + "[public]", expected.getPublic(), actual.getPublic());
+ assertKeyEquals(message + "[private]", expected.getPrivate(), actual.getPrivate());
+ }
+
+ public static <T extends Key> void assertKeyEquals(String message, T expected, T actual) {
+ if (expected == actual) {
+ return;
+ }
+
+ assertEquals(message + "[algorithm]", expected.getAlgorithm(), actual.getAlgorithm());
+
+ if (expected instanceof RSAPublicKey) {
+ assertRSAPublicKeyEquals(message, RSAPublicKey.class.cast(expected), RSAPublicKey.class.cast(actual));
+ } else if (expected instanceof DSAPublicKey) {
+ assertDSAPublicKeyEquals(message, DSAPublicKey.class.cast(expected), DSAPublicKey.class.cast(actual));
+ } else if (expected instanceof ECPublicKey) {
+ assertECPublicKeyEquals(message, ECPublicKey.class.cast(expected), ECPublicKey.class.cast(actual));
+ } else if (expected instanceof RSAPrivateKey) {
+ assertRSAPrivateKeyEquals(message, RSAPrivateKey.class.cast(expected), RSAPrivateKey.class.cast(actual));
+ } else if (expected instanceof ECPrivateKey) {
+ assertECPrivateKeyEquals(message, ECPrivateKey.class.cast(expected), ECPrivateKey.class.cast(actual));
+ }
+ assertArrayEquals(message + "[encdoded-data]", expected.getEncoded(), actual.getEncoded());
+ }
+
+ public static void assertRSAPublicKeyEquals(String message, RSAPublicKey expected, RSAPublicKey actual) {
+ if (expected == actual) {
+ return;
+ }
+
+ assertEquals(message + "[e]", expected.getPublicExponent(), actual.getPublicExponent());
+ assertEquals(message + "[n]", expected.getModulus(), actual.getModulus());
+ }
+
+ public static void assertDSAPublicKeyEquals(String message, DSAPublicKey expected, DSAPublicKey actual) {
+ if (expected == actual) {
+ return;
+ }
+
+ assertEquals(message + "[y]", expected.getY(), actual.getY());
+ assertDSAParamsEquals(message + "[params]", expected.getParams(), actual.getParams());
+ }
+
+ public static void assertECPublicKeyEquals(String message, ECPublicKey expected, ECPublicKey actual) {
+ if (expected == actual) {
+ return;
+ }
+
+ assertECPointEquals(message + "[W]", expected.getW(), actual.getW());
+ assertECParameterSpecEquals(message, expected, actual);
+ }
+
+ public static void assertRSAPrivateKeyEquals(String message, RSAPrivateKey expected, RSAPrivateKey actual) {
+ if (expected == actual) {
+ return;
+ }
+
+ assertEquals(message + "[d]", expected.getPrivateExponent(), actual.getPrivateExponent());
+ assertEquals(message + "[n]", expected.getModulus(), actual.getModulus());
+ }
+
+ public static void assertDSAPrivateKeyEquals(String message, DSAPrivateKey expected, DSAPrivateKey actual) {
+ if (expected == actual) {
+ return;
+ }
+
+ assertEquals(message + "[x]", expected.getX(), actual.getX());
+ assertDSAParamsEquals(message + "[params]", expected.getParams(), actual.getParams());
+ }
+
+ public static void assertDSAParamsEquals(String message, DSAParams expected, DSAParams actual) {
+ if (expected == actual) {
+ return;
+ }
+
+ assertEquals(message + "[g]", expected.getG(), actual.getG());
+ assertEquals(message + "[p]", expected.getP(), actual.getP());
+ assertEquals(message + "[q]", expected.getQ(), actual.getQ());
+ }
+
+ public static void assertECPrivateKeyEquals(String message, ECPrivateKey expected, ECPrivateKey actual) {
+ if (expected == actual) {
+ return;
+ }
+
+ assertEquals(message + "[S]", expected.getS(), actual.getS());
+ assertECParameterSpecEquals(message, expected, actual);
+ }
+
+ public static void assertECParameterSpecEquals(String message, ECKey expected, ECKey actual) {
+ if (expected == actual) {
+ return;
+ }
+ assertECParameterSpecEquals(message, expected.getParams(), actual.getParams());
+ }
+
+ public static void assertECParameterSpecEquals(String message, ECParameterSpec expected, ECParameterSpec actual) {
+ if (expected == actual) {
+ return;
+ }
+
+ assertEquals(message + "[order]", expected.getOrder(), actual.getOrder());
+ assertEquals(message + "[cofactor]", expected.getCofactor(), actual.getCofactor());
+ assertECPointEquals(message + "[generator]", expected.getGenerator(), actual.getGenerator());
+ assertCurveEquals(message + "[curve]", expected.getCurve(), actual.getCurve());
+ }
+
+ public static void assertCurveEquals(String message, EllipticCurve expected, EllipticCurve actual) {
+ if (expected == actual) {
+ return;
+ }
+
+ assertEquals(message + "[A]", expected.getA(), actual.getA());
+ assertEquals(message + "[B]", expected.getB(), actual.getB());
+ assertArrayEquals(message + "[seed]", expected.getSeed(), actual.getSeed());
+ assertECFieldEquals(message + "[field]", expected.getField(), actual.getField());
+ }
+
+ public static void assertECFieldEquals(String message, ECField expected, ECField actual) {
+ if (expected == actual) {
+ return;
+ }
+
+ assertEquals(message + "[size]", expected.getFieldSize(), actual.getFieldSize());
+ }
+
+ public static void assertECPointEquals(String message, ECPoint expected, ECPoint actual) {
+ if (expected == actual) {
+ return;
+ }
+
+ assertEquals(message + "[x]", expected.getAffineX(), actual.getAffineX());
+ assertEquals(message + "[y]", expected.getAffineY(), actual.getAffineY());
+ }
+
+ public static void assertFileLength(File file, long length, long timeout) throws Exception {
+ assertFileLength(file.toPath(), length, timeout);
+ }
+
+ /**
+ * Waits the specified timeout for the file to exist and have the required length
+ *
+ * @param file The file {@link Path} to check
+ * @param length Expected length
+ * @param timeout Timeout (msec.) to wait for satisfying the requirements
+ * @throws Exception If failed to access the file
+ */
+ public static void assertFileLength(Path file, long length, long timeout) throws Exception {
+ if (waitForFile(file, length, timeout)) {
+ return;
+ }
+ assertTrue("File not found: " + file, Files.exists(file));
+ assertEquals("Mismatched file size for " + file, length, Files.size(file));
+ }
+
+ public static boolean waitForFile(Path file, long length, long timeout) throws Exception {
+ while (timeout > 0L) {
+ long sleepTime = Math.min(timeout, 100L);
+ if (Files.exists(file) && (Files.size(file) == length)) {
+ return true;
+ }
+
+ long sleepStart = System.nanoTime();
+ Thread.sleep(sleepTime);
+ long sleepEnd = System.nanoTime();
+ long nanoSleep = sleepEnd - sleepStart;
+
+ sleepTime = TimeUnit.NANOSECONDS.toMillis(nanoSleep);
+ timeout -= sleepTime;
+ }
+
+ return false;
+ }
+
+ public static void outputDebugMessage(String format, Object... args) {
+ if (OUTPUT_DEBUG_MESSAGES) {
+ outputDebugMessage(String.format(format, args));
+ }
+ }
+
+ public static void outputDebugMessage(Object message) {
+ if (OUTPUT_DEBUG_MESSAGES) {
+ System.out.append("===[DEBUG]=== ").println(message);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/util/test/NoIoTestCase.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/NoIoTestCase.java b/sshd-common/src/test/java/org/apache/sshd/util/test/NoIoTestCase.java
new file mode 100644
index 0000000..7d52892
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/NoIoTestCase.java
@@ -0,0 +1,30 @@
+/*
+ * 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.util.test;
+
+/**
+ * Marker interface used as <A HREF="https://github.com/junit-team/junit4/wiki/categories">jUnit category</A>
+ * to indicate a test that does not require real client/server interaction.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface NoIoTestCase {
+ // Marker interface
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java b/sshd-common/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
new file mode 100644
index 0000000..b4eb84c
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
@@ -0,0 +1,56 @@
+/*
+ * 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.util.test;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class OutputCountTrackingOutputStream extends FilterOutputStream {
+ protected long writeCount;
+
+ public OutputCountTrackingOutputStream(OutputStream out) {
+ super(out);
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ out.write(b);
+ updateWriteCount(1L);
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ out.write(b, off, len); // don't call super since it calls the single 'write'
+ updateWriteCount(len);
+ }
+
+ public long getWriteCount() {
+ return writeCount;
+ }
+
+ protected long updateWriteCount(long delta) {
+ writeCount += delta;
+ return writeCount;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/util/test/TeeOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/util/test/TeeOutputStream.java b/sshd-common/src/test/java/org/apache/sshd/util/test/TeeOutputStream.java
new file mode 100644
index 0000000..4cd6014
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/util/test/TeeOutputStream.java
@@ -0,0 +1,64 @@
+/*
+ * 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.util.test;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class TeeOutputStream extends OutputStream {
+
+ private OutputStream[] tees;
+
+ public TeeOutputStream(OutputStream... tees) {
+ this.tees = tees;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ for (OutputStream s : tees) {
+ s.write(b);
+ }
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ for (OutputStream s : tees) {
+ s.write(b, off, len);
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ for (OutputStream s : tees) {
+ s.flush();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ for (OutputStream s : tees) {
+ s.close();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/log4j.properties b/sshd-common/src/test/resources/log4j.properties
new file mode 100644
index 0000000..cf1d08a
--- /dev/null
+++ b/sshd-common/src/test/resources/log4j.properties
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+#
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+#
+# The logging properties used during tests..
+#
+log4j.rootLogger=INFO, stdout, logfile
+#log4j.logger.org.apache.sshd=TRACE
+#log4j.logger.org.apache.sshd.common.channel.Window=DEBUG
+
+# CONSOLE appender
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n
+
+# File appender
+log4j.appender.logfile=org.apache.log4j.FileAppender
+log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
+log4j.appender.logfile.layout.ConversionPattern=%d | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n
+log4j.appender.logfile.file=target/sshd-common-tests.log
+log4j.appender.logfile.append=true
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadGlobalHostsConfigEntries.config.txt
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadGlobalHostsConfigEntries.config.txt b/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadGlobalHostsConfigEntries.config.txt
new file mode 100644
index 0000000..5f772cd
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadGlobalHostsConfigEntries.config.txt
@@ -0,0 +1,22 @@
+# Global section first
+
+ User global
+ Port 7365
+ IdentityFile ~/.ssh/github.key
+ HostName 37.77.34.7
+
+# User override
+Host *.apache.org
+ User mina-sshd
+
+# Port override
+Host *.github.com
+ Port 443
+
+# Host name override
+Host 10.*.*.*
+ HostName 7.3.6.5
+
+# Identity override
+Host 192.168.*.*
+ IdentityFile ~/.ssh/internal.key
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadMultipleHostPatterns.config.txt
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadMultipleHostPatterns.config.txt b/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadMultipleHostPatterns.config.txt
new file mode 100644
index 0000000..0f5edcb
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadMultipleHostPatterns.config.txt
@@ -0,0 +1,5 @@
+# Same configuration duplicated in multiple configuration entries
+Host *.github.com *.apache.org 10.*.*.*
+ User mina-sshd
+ Port 443
+ HostName 7.3.6.5
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadSimpleHostsConfigEntries.config.txt
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadSimpleHostsConfigEntries.config.txt b/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadSimpleHostsConfigEntries.config.txt
new file mode 100644
index 0000000..1058777
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/client/config/hosts/testReadSimpleHostsConfigEntries.config.txt
@@ -0,0 +1,8 @@
+# Simple configuration file
+Host *.github.com
+ User mina-sshd
+ Port 443
+ IdentityFile ~/.ssh/github.key
+
+Host *.apache.org
+ User mina-sshd
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_dsa
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_dsa b/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_dsa
new file mode 100644
index 0000000..1d3ef24
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_dsa
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBvAIBAAKBgQDIPyMbBuQcZxeYDOyCqqkdK37cWQvp+RpWzvieB/oiG/ykfDQX
+oZMRtwqwWTBfejNitbBBmC6G/t5OK+9aFmj7pfJ+a7fZKXfiUquIg9soDsoOindf
+2AwR6MZ3os8uiP2xrC8IQAClnETa15mFShs4a4b2VjddgCQ6tphnY97MywIVAPtr
+YyW11RIXsVTf/9KlbhYaNlt5AoGAX9JzbHykC/0xDKOyKU6xDIOVdEZ0ooAl9Psl
+BEUuNhlv2XgmQScO6C9l2W7gbbut7zIw4FaZ2/dgXa3D4IyexBVug5XMnrssErZo
+NcoF5g0dgEAsb9Hl9gkIK3VHM5kWteeUg1VE700JTtSMisdL8CgIdR+xN8iVH5Ew
+CbLWxmECgYEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+ab
+YpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLa
+XWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7ACFQCE6flG
+nmVCAbzo9YsbdJWBnxMnBA==
+-----END DSA PRIVATE KEY-----
\ No newline at end of file
[31/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java b/sshd-common/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
new file mode 100644
index 0000000..0ea58f7
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.signature;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.signature.BuiltinSignatures.ParseResult;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+import org.mockito.Mockito;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BuiltinSignaturesTest extends JUnitTestSupport {
+ public BuiltinSignaturesTest() {
+ super();
+ }
+
+ @Test
+ public void testFromName() {
+ for (BuiltinSignatures expected : BuiltinSignatures.VALUES) {
+ String name = expected.getName();
+ BuiltinSignatures actual = BuiltinSignatures.fromFactoryName(name);
+ assertSame(name, expected, actual);
+ }
+ }
+
+ @Test
+ public void testParseSignaturesList() {
+ List<String> builtin = NamedResource.getNameList(BuiltinSignatures.VALUES);
+ List<String> unknown = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
+ Random rnd = new Random();
+ for (int index = 0; index < (builtin.size() + unknown.size()); index++) {
+ Collections.shuffle(builtin, rnd);
+ Collections.shuffle(unknown, rnd);
+
+ List<String> weavedList = new ArrayList<>(builtin.size() + unknown.size());
+ for (int bIndex = 0, uIndex = 0; (bIndex < builtin.size()) || (uIndex < unknown.size());) {
+ boolean useBuiltin = false;
+ if (bIndex < builtin.size()) {
+ useBuiltin = uIndex >= unknown.size() || rnd.nextBoolean();
+ }
+
+ if (useBuiltin) {
+ weavedList.add(builtin.get(bIndex));
+ bIndex++;
+ } else if (uIndex < unknown.size()) {
+ weavedList.add(unknown.get(uIndex));
+ uIndex++;
+ }
+ }
+
+ String fullList = GenericUtils.join(weavedList, ',');
+ ParseResult result = BuiltinSignatures.parseSignatureList(fullList);
+ List<String> parsed = NamedResource.getNameList(result.getParsedFactories());
+ List<String> missing = result.getUnsupportedFactories();
+
+ // makes sure not only that the contents are the same but also the order
+ assertListEquals(fullList + "[parsed]", builtin, parsed);
+ assertListEquals(fullList + "[unsupported]", unknown, missing);
+ }
+ }
+
+ @Test
+ public void testResolveFactoryOnBuiltinValues() {
+ for (SignatureFactory expected : BuiltinSignatures.VALUES) {
+ String name = expected.getName();
+ SignatureFactory actual = BuiltinSignatures.resolveFactory(name);
+ assertSame(name, expected, actual);
+ }
+ }
+
+ @Test
+ public void testNotAllowedToRegisterBuiltinFactories() {
+ for (SignatureFactory expected : BuiltinSignatures.VALUES) {
+ try {
+ BuiltinSignatures.registerExtension(expected);
+ fail("Unexpected success for " + expected.getName());
+ } catch (IllegalArgumentException e) {
+ // expected - ignored
+ }
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNotAllowedToOverrideRegisteredFactories() {
+ SignatureFactory expected = Mockito.mock(SignatureFactory.class);
+ Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+ String name = expected.getName();
+ try {
+ for (int index = 1; index <= Byte.SIZE; index++) {
+ BuiltinSignatures.registerExtension(expected);
+ assertEquals("Unexpected success at attempt #" + index, 1, index);
+ }
+ } finally {
+ BuiltinSignatures.unregisterExtension(name);
+ }
+ }
+
+ @Test
+ public void testResolveFactoryOnRegisteredExtension() {
+ SignatureFactory expected = Mockito.mock(SignatureFactory.class);
+ Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+ String name = expected.getName();
+ try {
+ assertNull("Extension already registered", BuiltinSignatures.resolveFactory(name));
+ BuiltinSignatures.registerExtension(expected);
+
+ SignatureFactory actual = BuiltinSignatures.resolveFactory(name);
+ assertSame("Mismatched resolved instance", expected, actual);
+ } finally {
+ SignatureFactory actual = BuiltinSignatures.unregisterExtension(name);
+ assertSame("Mismatched unregistered instance", expected, actual);
+ assertNull("Extension not un-registered", BuiltinSignatures.resolveFactory(name));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java
new file mode 100644
index 0000000..d9f507d
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.signature;
+
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.spec.DSAPublicKeySpec;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class SignatureDSATest extends JUnitTestSupport {
+ public SignatureDSATest() {
+ super();
+ }
+
+ @Test
+ public void testTooShortSignature() throws Exception {
+ KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
+ SignatureDSA signatureDSA = new SignatureDSA(KeyUtils.DSS_ALGORITHM) {
+ @Override
+ protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
+ return java.security.Signature.getInstance(algo);
+
+ }
+ };
+
+ byte[] y = new byte[] {
+ 0, -92, 59, 5, 72, 124, 101, 124, -18, 114, 7, 100, 98, -61, 73, -104,
+ 120, -98, 54, 118, 17, -62, 91, -110, 29, 98, 50, -101, -41, 99, -116,
+ 101, 107, -123, 124, -97, 62, 119, 88, -109, -110, -1, 109, 119, -51,
+ 69, -98, -105, 2, -69, -121, -82, -118, 23, -6, 96, -61, -65, 102, -58,
+ -74, 32, -104, 116, -6, -35, -83, -10, -88, -68, 106, -112, 72, -2, 35,
+ 38, 15, -11, -22, 30, -114, -46, -47, -18, -17, -71, 24, -25, 28, 13, 29,
+ -40, 101, 18, 81, 45, -120, -67, -53, -41, 11, 50, -89, -33, 50, 54, -14,
+ -91, -35, 12, -42, 13, -84, -19, 100, -3, -85, -18, 74, 99, -49, 64, -49,
+ 51, -83, -82, -127, 116, 64 };
+ byte[] p = new byte[] {
+ 0, -3, 127, 83, -127, 29, 117, 18, 41, 82, -33, 74, -100, 46, -20, -28,
+ -25, -10, 17, -73, 82, 60, -17, 68, 0, -61, 30, 63, -128, -74, 81, 38,
+ 105, 69, 93, 64, 34, 81, -5, 89, 61, -115, 88, -6, -65, -59, -11, -70,
+ 48, -10, -53, -101, 85, 108, -41, -127, 59, -128, 29, 52, 111, -14, 102,
+ 96, -73, 107, -103, 80, -91, -92, -97, -97, -24, 4, 123, 16, 34, -62, 79,
+ -69, -87, -41, -2, -73, -58, 27, -8, 59, 87, -25, -58, -88, -90, 21, 15, 4,
+ -5, -125, -10, -45, -59, 30, -61, 2, 53, 84, 19, 90, 22, -111, 50, -10, 117,
+ -13, -82, 43, 97, -41, 42, -17, -14, 34, 3, 25, -99, -47, 72, 1, -57 };
+ byte[] q = new byte[] {
+ 0, -105, 96, 80, -113, 21, 35, 11, -52, -78, -110, -71, -126, -94, -21,
+ -124, 11, -16, 88, 28, -11 };
+ byte[] g = new byte[] {
+ 0, -9, -31, -96, -123, -42, -101, 61, -34, -53, -68, -85, 92, 54, -72, 87,
+ -71, 121, -108, -81, -69, -6, 58, -22, -126, -7, 87, 76, 11, 61, 7, -126,
+ 103, 81, 89, 87, -114, -70, -44, 89, 79, -26, 113, 7, 16, -127, -128, -76,
+ 73, 22, 113, 35, -24, 76, 40, 22, 19, -73, -49, 9, 50, -116, -56, -90, -31,
+ 60, 22, 122, -117, 84, 124, -115, 40, -32, -93, -82, 30, 43, -77, -90, 117,
+ -111, 110, -93, 127, 11, -6, 33, 53, 98, -15, -5, 98, 122, 1, 36, 59, -52,
+ -92, -15, -66, -88, 81, -112, -119, -88, -125, -33, -31, 90, -27, -97, 6,
+ -110, -117, 102, 94, -128, 123, 85, 37, 100, 1, 76, 59, -2, -49, 73, 42 };
+
+ BigInteger bigY = new BigInteger(y);
+ BigInteger bigP = new BigInteger(p);
+ BigInteger bigQ = new BigInteger(q);
+ BigInteger bigG = new BigInteger(g);
+
+ DSAPublicKeySpec dsaPublicKey = new DSAPublicKeySpec(bigY, bigP, bigQ, bigG);
+ signatureDSA.initVerifier(kf.generatePublic(dsaPublicKey));
+ byte[] h = new byte[] {
+ -4, 111, -103, 111, 72, -106, 105, -19, 81, -123, 84, -13, -40, -53, -3,
+ -97, -8, 43, -22, -2, -23, -15, 28, 116, -63, 96, -79, -127, -84, 63, -6, -94 };
+ signatureDSA.update(h);
+
+ byte[] sig_of_h = new byte[] {
+ 0, 0, 0, 7, 115, 115, 104, 45, 100, 115, 115, 0, 0, 0, 40, 0, 79,
+ 84, 118, -50, 11, -117, -112, 52, -25, -78, -50, -20, 6, -69, -26,
+ 7, 90, -34, -124, 80, 76, -32, -23, -8, 43, 38, -48, -89, -17, -60,
+ -1, -78, 112, -88, 14, -39, -78, -98, -80 };
+ boolean verified = signatureDSA.verify(sig_of_h);
+
+ assertTrue(verified);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
new file mode 100644
index 0000000..5671ac3
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.signature;
+
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Base64;
+import java.util.Map;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class SignatureRSATest extends JUnitTestSupport {
+ private static final Base64.Decoder B64_DECODER = Base64.getDecoder();
+ @SuppressWarnings("checkstyle:linelength")
+ private static final byte[] TEST_MSG =
+ B64_DECODER.decode("AAAAFPHgK1MeV9zNnok3pwNJhCd8SONqMgAAAAlidWlsZHVzZXIAAAAOc3NoLWNvbm5lY3Rpb24AAAAJcHVibGlja2V5AQAAAAdzc2gtcnNhAAABFQAAAAdzc2gtcnNhAAAAASMAAAEBAMs9HO/NH/Now+6fSnESebaG4wzaYQWA1b/q1TGV1wHNtCg9fGFGVSKs0VxKF4cfVyrSLtgLjnlXQTn+Lm7xiYKGbBbsTQWOqEDaBVBsRbAkxIkpuvr6/EBxwrtDbKmSQYTJZVJSD2bZRYjGsR9gpZXPorOOKFd5EPCMHXsqnhp2hidTGH7cK6RuLk7MNnPISsY0Nbx8/ZvikiPROGcoTZ8bzUv4IaLr3veW6epSeQem8tJqhnrpTHhbLU99zf045M0Gsnk/azjjlBM+qrHZ5FNdC1kowJnLtf2Oy/rUQNpkGJtcBPT8xvreV0wLsn9t3hSxzsc0+VkDNTQRlfU+o3M=");
+ @SuppressWarnings("checkstyle:linelength")
+ private static final byte[] TEST_SIGNATURE =
+ B64_DECODER.decode("AAAAB3NzaC1yc2EAAAD/+Ntnf4qfr2J1voDS6I+u3VRjtMn+LdWJsAZfkLDxRkK1rQxP7QAjLdNqpT4CkWHp8dtoTGFlBFt6NieNJCMTA2KSOxJMZKsX7e/lHkh7C+vhQvJ9eLTKWjCxSFUrcM0NvFhmwbRCffwXSHvAKak4wbmofxQMpd+G4jZkNMz5kGpmeICBcNjRLPb7oXzuGr/g4x/3ge5Qaawqrg/gcZr/sKN6SdE8SszgKYO0SB320N4gcUoShVdLYr9uwdJ+kJoobfkUK6Or171JCctP/cu2nM79lDqVnJw/2jOG8OnTc8zRDXAh0RKoR5rOU8cOHm0Ls2MATsFdnyRU5FGUxqZ+");
+ private static PublicKey testKey;
+
+ public SignatureRSATest() {
+ super();
+ }
+
+ @BeforeClass
+ public static void initializeTestKey() throws GeneralSecurityException {
+ byte[] exp = B64_DECODER.decode("Iw==");
+ @SuppressWarnings("checkstyle:linelength")
+ byte[] mod = B64_DECODER.decode("AMs9HO/NH/Now+6fSnESebaG4wzaYQWA1b/q1TGV1wHNtCg9fGFGVSKs0VxKF4cfVyrSLtgLjnlXQTn+Lm7xiYKGbBbsTQWOqEDaBVBsRbAkxIkpuvr6/EBxwrtDbKmSQYTJZVJSD2bZRYjGsR9gpZXPorOOKFd5EPCMHXsqnhp2hidTGH7cK6RuLk7MNnPISsY0Nbx8/ZvikiPROGcoTZ8bzUv4IaLr3veW6epSeQem8tJqhnrpTHhbLU99zf045M0Gsnk/azjjlBM+qrHZ5FNdC1kowJnLtf2Oy/rUQNpkGJtcBPT8xvreV0wLsn9t3hSxzsc0+VkDNTQRlfU+o3M=");
+ KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
+ testKey = kf.generatePublic(new RSAPublicKeySpec(new BigInteger(mod), new BigInteger(exp)));
+ }
+
+ @Test // see SSHD-642
+ public void testLeadingZeroesBC() throws Throwable {
+ testLeadingZeroes(new Factory<SignatureRSA>() {
+ @Override
+ public SignatureRSA create() {
+ return new SignatureRSA() {
+ @Override
+ protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
+ assertFalse("Signature not initialized for verification", forSigning);
+ java.security.Signature signature = super.doInitSignature(algo, forSigning);
+ if (SecurityUtils.isBouncyCastleRegistered()) {
+ Provider provider = signature.getProvider();
+ String name = provider.getName();
+ assertEquals("Mismatched BC provider name", SecurityUtils.BOUNCY_CASTLE, name);
+ }
+ return signature;
+ }
+ };
+ }
+ });
+ }
+
+ @Test // see SSHD-642
+ public void testLeadingZeroesJCE() throws Throwable {
+ testLeadingZeroes(() -> new SignatureRSA() {
+ @Override
+ protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
+ assertFalse("Signature not initialized for verification", forSigning);
+ java.security.Signature signature = java.security.Signature.getInstance(algo);
+ Provider provider = signature.getProvider();
+ String name = provider.getName();
+ assertNotEquals("BC provider used although not required", SecurityUtils.BOUNCY_CASTLE, name);
+ return signature;
+ }
+ });
+ }
+
+ private void testLeadingZeroes(Factory<? extends SignatureRSA> factory) throws Exception {
+ SignatureRSA rsa = factory.create();
+ rsa.initVerifier(testKey);
+
+ int vSize = rsa.getVerifierSignatureSize();
+ assertTrue("Verifier signature size not initialized", vSize > 0);
+
+ // make sure padding is required
+ Map.Entry<String, byte[]> encoding = rsa.extractEncodedSignature(TEST_SIGNATURE);
+ assertNotNull("Signature is not encoded", encoding);
+ byte[] data = encoding.getValue();
+ assertTrue("Signature data size (" + data.length + ") not below verifier size (" + vSize + ")", data.length < vSize);
+
+ rsa.update(TEST_MSG);
+ assertTrue("Failed to verify", rsa.verify(TEST_SIGNATURE));
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java
new file mode 100644
index 0000000..2e15341
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.signature;
+
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.junit.Ignore;
+
+/**
+ * A "scratch-pad" class for testing signatures related code during development
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@Ignore("Used only for development")
+public class SignaturesDevelopment extends JUnitTestSupport {
+ public SignaturesDevelopment() {
+ super();
+ }
+
+ public static void testSignatureFactory(
+ SignatureFactory factory, KeyPair kp, byte[] data, boolean generateSignature, byte[] signature)
+ throws Exception {
+ Signature signer = factory.create();
+ if (generateSignature) {
+ signer.initSigner(kp.getPrivate());
+ signer.update(data);
+ signature = signer.sign();
+ System.out.append('\t').append("Signature: ").println(BufferUtils.toHex(':', signature));
+ } else {
+ signer.initVerifier(kp.getPublic());
+ signer.update(data);
+ if (signer.verify(signature)) {
+ System.out.append('\t').println("Valid signature");
+ } else {
+ System.err.append('\t').println("Invalid signature");
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+
+ // args[0]=signatureName, args[1]=publicKey, args[2]=privateKey, args[3]=sign/verify, args[4]=data, args[5]=signature(if verify required)
+ public static void main(String[] args) throws Exception {
+ SignatureFactory factory = BuiltinSignatures.resolveFactory(args[0]);
+ // TODO recover public/private keys according to factory name
+ byte[] publicKey = BufferUtils.decodeHex(':', args[1]);
+ PublicKey pubKey = EdDSASecurityProviderUtils.generateEDDSAPublicKey(publicKey);
+ byte[] privateKey = BufferUtils.decodeHex(':', args[2]);
+ PrivateKey prvKey = EdDSASecurityProviderUtils.generateEDDSAPrivateKey(privateKey);
+ String op = args[3];
+ byte[] data = BufferUtils.decodeHex(':', args[4]);
+ byte[] signature = GenericUtils.EMPTY_BYTE_ARRAY;
+ if ("verify".equalsIgnoreCase(op)) {
+ signature = BufferUtils.decodeHex(':', args[5]);
+ }
+
+ testSignatureFactory(factory, new KeyPair(pubKey, prvKey), data, "sign".equalsIgnoreCase(op), signature);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
new file mode 100644
index 0000000..1619cd3
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EventListener;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class EventListenerUtilsTest extends JUnitTestSupport {
+ public EventListenerUtilsTest() {
+ super();
+ }
+
+ @Test
+ public void testProxyWrapper() {
+ List<ProxyListenerImpl> impls = new ArrayList<>();
+ for (int index = 0; index < Byte.SIZE; index++) {
+ impls.add(new ProxyListenerImpl());
+ }
+
+ ProxyListener listener = EventListenerUtils.proxyWrapper(ProxyListener.class, impls);
+ String expStr = getCurrentTestName();
+ Number expNum = System.currentTimeMillis();
+ listener.callMeWithString(expStr);
+ listener.callMeWithNumber(expNum);
+
+ for (int index = 0; index < impls.size(); index++) {
+ ProxyListenerImpl l = impls.get(index);
+ assertSame("Mismatched string at listener #" + index, expStr, l.getStringValue());
+ assertSame("Mismatched number at listener #" + index, expNum, l.getNumberValue());
+ }
+ }
+
+ @Test
+ public void testListenerInstanceComparatorOnProxy() {
+ Comparator<? super EventListener> comparator = EventListenerUtils.LISTENER_INSTANCE_COMPARATOR;
+ ProxyListener p1 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
+ assertEquals("Mismatched self reference comparison", 0, comparator.compare(p1, p1));
+
+ EventListener l = new EventListener() { /* nothing extra */ };
+ assertEquals("Mismatched proxy vs. non-proxy result", 1, Integer.signum(comparator.compare(p1, l)));
+ assertEquals("Mismatched non-proxy vs. proxy result", -1, Integer.signum(comparator.compare(l, p1)));
+
+ ProxyListener p2 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
+ int p1vsp2 = Integer.signum(comparator.compare(p1, p2));
+ assertNotEquals("Mismatched p1 vs. p2 comparison", 0, p1vsp2);
+ assertEquals("Mismatched p2 vs. p1 comparison result", 0 - p1vsp2, Integer.signum(comparator.compare(p2, p1)));
+ }
+
+ @Test
+ public void testListenerInstanceComparatorOnNonProxy() {
+ Comparator<? super EventListener> comparator = EventListenerUtils.LISTENER_INSTANCE_COMPARATOR;
+ EventListener l1 = new EventListener() { /* nothing extra */ };
+ assertEquals("Mismatched self reference comparison", 0, comparator.compare(l1, l1));
+
+ EventListener l2 = new EventListener() { /* nothing extra */ };
+ int l1vsl2 = Integer.signum(comparator.compare(l1, l2));
+ assertNotEquals("Mismatched l1 vs. l2 comparison result", 0, l1vsl2);
+ assertEquals("Mismatched l2 vs. l1 comparison result", 0 - l1vsl2, Integer.signum(comparator.compare(l2, l1)));
+ }
+
+ @Test
+ public void testSynchronizedListenersSetOnProxies() {
+ ProxyListener p1 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
+ Set<ProxyListener> s = EventListenerUtils.synchronizedListenersSet();
+ for (int index = 1; index <= Byte.SIZE; index++) {
+ boolean modified = s.add(p1);
+ assertEquals("Mismatched p1 modification indicator at attempt #" + index, index == 1, modified);
+ assertEquals("Mismatched p1 set size at attempt #" + index, 1, s.size());
+ }
+
+ ProxyListener p2 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
+ for (int index = 1; index <= Byte.SIZE; index++) {
+ boolean modified = s.add(p2);
+ assertEquals("Mismatched p2 modification indicator at attempt #" + index, index == 1, modified);
+ assertEquals("Mismatched p2 set size at attempt #" + index, 2, s.size());
+ }
+
+ assertTrue("Failed to remove p1", s.remove(p1));
+ assertEquals("Mismatched post p1-remove size", 1, s.size());
+ assertTrue("Failed to remove p2", s.remove(p2));
+ assertEquals("Mismatched post p2-remove size", 0, s.size());
+ }
+
+ interface ProxyListener extends SshdEventListener {
+ void callMeWithString(String s);
+
+ void callMeWithNumber(Number n);
+ }
+
+ static class ProxyListenerImpl implements ProxyListener {
+ private String strValue;
+ private Number numValue;
+
+ ProxyListenerImpl() {
+ super();
+ }
+
+ public String getStringValue() {
+ return strValue;
+ }
+
+ @Override
+ public void callMeWithString(String s) {
+ strValue = s;
+ }
+
+ public Number getNumberValue() {
+ return numValue;
+ }
+
+ @Override
+ public void callMeWithNumber(Number n) {
+ numValue = n;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
new file mode 100644
index 0000000..5727e88
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category(NoIoTestCase.class)
+public class GenericUtilsTest extends JUnitTestSupport {
+ public GenericUtilsTest() {
+ super();
+ }
+
+ @Test
+ public void testSplitAndJoin() {
+ List<String> expected = Collections.unmodifiableList(
+ Arrays.asList(getClass().getPackage().getName().replace('.', '/'), getClass().getSimpleName(), getCurrentTestName()));
+
+ // NOTE: we also test characters that have meaning in String.split(...) as regex ones
+ for (char ch : new char[]{',', '.', '*', '?'}) {
+ String sep = String.valueOf(ch);
+ String s = GenericUtils.join(expected, sep);
+ String[] actual = GenericUtils.split(s, ch);
+ assertEquals("Mismatched split length for separator=" + sep, expected.size(), GenericUtils.length((Object[]) actual));
+
+ for (int index = 0; index < actual.length; index++) {
+ String e = expected.get(index);
+ String a = actual[index];
+ if (!e.endsWith(a)) {
+ fail("Mismatched value at index=" + index + " for separator=" + sep + ": expected=" + e + ", actual=" + a);
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testStripQuotes() {
+ String expected = getCurrentTestName();
+ assertSame("Unexpected un-quoted stripping", expected, GenericUtils.stripQuotes(expected));
+
+ StringBuilder sb = new StringBuilder(2 + expected.length()).append('|').append(expected).append('|');
+ for (int index = 0; index < GenericUtils.QUOTES.length(); index++) {
+ char delim = GenericUtils.QUOTES.charAt(index);
+ sb.setCharAt(0, delim);
+ sb.setCharAt(sb.length() - 1, delim);
+
+ CharSequence actual = GenericUtils.stripQuotes(sb);
+ assertEquals("Mismatched result for delim (" + delim + ")", expected, actual.toString());
+ }
+ }
+
+ @Test
+ public void testStripOnlyFirstLayerQuotes() {
+ StringBuilder sb = new StringBuilder().append("||").append(getCurrentTestName()).append("||");
+ char[] delims = {'\'', '"', '"', '\''};
+ for (int index = 0; index < delims.length; index += 2) {
+ char topDelim = delims[index];
+ char innerDelim = delims[index + 1];
+ sb.setCharAt(0, topDelim);
+ sb.setCharAt(1, innerDelim);
+ sb.setCharAt(sb.length() - 2, innerDelim);
+ sb.setCharAt(sb.length() - 1, topDelim);
+
+ CharSequence expected = sb.subSequence(1, sb.length() - 1);
+ CharSequence actual = GenericUtils.stripQuotes(sb);
+ assertEquals("Mismatched result for delim (" + topDelim + "/" + innerDelim + ")", expected.toString(), actual.toString());
+ }
+ }
+
+ @Test
+ public void testStripDelimiters() {
+ String expected = getCurrentTestName();
+ final char delim = '|';
+ assertSame("Unexpected un-delimited stripping", expected, GenericUtils.stripDelimiters(expected, delim));
+
+ CharSequence actual = GenericUtils.stripDelimiters(
+ new StringBuilder(2 + expected.length()).append(delim).append(expected).append(delim), delim);
+ assertEquals("Mismatched stripped values", expected, actual.toString());
+ }
+
+ @Test
+ public void testStripDelimitersOnlyIfOnBothEnds() {
+ final char delim = '$';
+ StringBuilder expected = new StringBuilder().append(delim).append(getCurrentTestName()).append(delim);
+ for (int index : new int[]{0, expected.length() - 1}) {
+ // restore original delimiters
+ expected.setCharAt(0, delim);
+ expected.setCharAt(expected.length() - 1, delim);
+ // trash one end
+ expected.setCharAt(index, (char) (delim + 1));
+
+ assertSame("Mismatched result for delim at index=" + index, expected, GenericUtils.stripDelimiters(expected, delim));
+ }
+ }
+
+ @Test
+ public void testAccumulateExceptionOnNullValues() {
+ assertNull("Unexpected null/null result", GenericUtils.accumulateException(null, null));
+
+ Throwable expected = new NoSuchMethodException(getClass().getName() + "#" + getCurrentTestName());
+ assertSame("Mismatched null/extra result", expected, GenericUtils.accumulateException(null, expected));
+ assertSame("Mismatched current/null result", expected, GenericUtils.accumulateException(expected, null));
+ }
+
+ @Test
+ public void testAccumulateExceptionOnExistingCurrent() {
+ RuntimeException[] expected = new RuntimeException[]{
+ new IllegalArgumentException(getCurrentTestName()),
+ new ClassCastException(getClass().getName()),
+ new NoSuchElementException(getClass().getPackage().getName())
+ };
+ RuntimeException current = new UnsupportedOperationException("top");
+ for (RuntimeException extra : expected) {
+ RuntimeException actual = GenericUtils.accumulateException(current, extra);
+ assertSame("Mismatched returned actual exception", current, actual);
+ }
+
+ Throwable[] actual = current.getSuppressed();
+ assertArrayEquals("Suppressed", expected, actual);
+ }
+
+ @Test
+ public void testNullOrEmptyCharArrayComparison() {
+ char[][] values = new char[][]{null, GenericUtils.EMPTY_CHAR_ARRAY};
+ for (char[] c1 : values) {
+ for (char[] c2 : values) {
+ assertEquals(((c1 == null) ? "null" : "empty") + " vs. " + ((c2 == null) ? "null" : "empty"), 0, GenericUtils.compare(c1, c2));
+ }
+ }
+ }
+
+ @Test
+ public void testCharArrayComparison() {
+ String s1 = getClass().getSimpleName();
+ char[] c1 = s1.toCharArray();
+ assertEquals("Same value equality", 0, GenericUtils.compare(c1, s1.toCharArray()));
+
+ String s2 = getCurrentTestName();
+ char[] c2 = s2.toCharArray();
+ assertEquals("s1 vs. s2", Integer.signum(s1.compareTo(s2)), Integer.signum(GenericUtils.compare(c1, c2)));
+ assertEquals("s2 vs. s1", Integer.signum(s2.compareTo(s1)), Integer.signum(GenericUtils.compare(c2, c1)));
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java
new file mode 100644
index 0000000..b0c48ca
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.Random;
+import java.util.function.IntUnaryOperator;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class Int2IntFunctionTest extends JUnitTestSupport {
+ public Int2IntFunctionTest() {
+ super();
+ }
+
+ @Test
+ public void testAdd() {
+ int factor = Byte.SIZE;
+ IntUnaryOperator func = Int2IntFunction.add(factor);
+ for (int index = 1, sum = 0; index <= Byte.SIZE; index++) {
+ sum = func.applyAsInt(sum);
+ assertEquals(factor * index, sum);
+ }
+ }
+
+ @Test
+ public void testAddIdentity() {
+ IntUnaryOperator func = Int2IntFunction.add(0);
+ Random rnd = new Random(System.nanoTime());
+ for (int index = 1; index <= Byte.SIZE; index++) {
+ int expected = rnd.nextInt();
+ int actual = func.applyAsInt(expected);
+ assertEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testSub() {
+ int factor = Byte.SIZE;
+ IntUnaryOperator func = Int2IntFunction.sub(factor);
+ for (int index = 1, sum = 0; index <= Byte.SIZE; index++) {
+ sum = func.applyAsInt(sum);
+ assertEquals(factor * index * -1, sum);
+ }
+ }
+
+ @Test
+ public void testSubIdentity() {
+ IntUnaryOperator func = Int2IntFunction.sub(0);
+ Random rnd = new Random(System.nanoTime());
+ for (int index = 1; index <= Byte.SIZE; index++) {
+ int expected = rnd.nextInt();
+ int actual = func.applyAsInt(expected);
+ assertEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testMul() {
+ int factor = 2;
+ IntUnaryOperator func = Int2IntFunction.mul(factor);
+ for (int index = 1, mul = 1, expected = factor; index <= Byte.SIZE; index++, expected *= factor) {
+ mul = func.applyAsInt(mul);
+ assertEquals(expected, mul);
+ }
+ }
+
+ @Test
+ public void testMulIdentity() {
+ IntUnaryOperator func = Int2IntFunction.mul(1);
+ Random rnd = new Random(System.nanoTime());
+ for (int index = 1; index <= Byte.SIZE; index++) {
+ int expected = rnd.nextInt();
+ int actual = func.applyAsInt(expected);
+ assertEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testMulZero() {
+ IntUnaryOperator func = Int2IntFunction.mul(0);
+ Random rnd = new Random(System.nanoTime());
+ for (int index = 1; index <= Byte.SIZE; index++) {
+ int value = rnd.nextInt();
+ int actual = func.applyAsInt(value);
+ assertEquals(Integer.toString(value), 0, actual);
+ }
+ }
+
+ @Test
+ public void testConstant() {
+ int expected = 377347;
+ IntUnaryOperator func = Int2IntFunction.constant(expected);
+ Random rnd = new Random(System.nanoTime());
+ for (int index = 1; index <= Byte.SIZE; index++) {
+ int value = rnd.nextInt();
+ int actual = func.applyAsInt(value);
+ assertEquals(Integer.toString(value), expected, actual);
+ }
+ }
+
+ @Test
+ public void testDiv() {
+ int factor = 2;
+ IntUnaryOperator func = Int2IntFunction.div(factor);
+ for (int index = 1, quot = 65536, expected = quot / factor; index <= Byte.SIZE; index++, expected /= factor) {
+ quot = func.applyAsInt(quot);
+ assertEquals(expected, quot);
+ }
+ }
+
+ @Test
+ public void testDivIdentity() {
+ IntUnaryOperator func = Int2IntFunction.div(1);
+ Random rnd = new Random(System.nanoTime());
+ for (int index = 1; index <= Byte.SIZE; index++) {
+ int expected = rnd.nextInt();
+ int actual = func.applyAsInt(expected);
+ assertEquals(expected, actual);
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDivZeroFactor() {
+ IntUnaryOperator func = Int2IntFunction.div(0);
+ fail("Unexpected success: " + func);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
new file mode 100644
index 0000000..927cecb
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class NumberUtilsTest extends JUnitTestSupport {
+ public NumberUtilsTest() {
+ super();
+ }
+
+ @Test
+ public void testPowersOf2List() {
+ assertEquals("Mismatched values size for " + NumberUtils.POWERS_OF_TWO, Long.SIZE, GenericUtils.size(NumberUtils.POWERS_OF_TWO));
+ long expected = 1L;
+ for (int index = 0; index < Long.SIZE; index++, expected <<= 1) {
+ Long actual = NumberUtils.POWERS_OF_TWO.get(index);
+ assertEquals("Mismatched value at index=" + index, Long.toHexString(expected), Long.toHexString(actual));
+ }
+ }
+
+ @Test
+ public void testNextPowerOf2() {
+ for (Long v : NumberUtils.POWERS_OF_TWO) {
+ long expected = v;
+ if (expected > 2L) {
+ assertEquals("Mismatched lower bound value", expected, NumberUtils.getNextPowerOf2(expected - 1L));
+ }
+
+ if (expected > 0L) { // avoid the negative value
+ assertEquals("Mismatched exact value", expected, NumberUtils.getNextPowerOf2(expected));
+ }
+ }
+ }
+
+ @Test
+ public void testToInteger() {
+ assertNull("Unexpected null value", NumberUtils.toInteger(null));
+ for (Number n : new Number[]{
+ Byte.valueOf(Byte.MAX_VALUE), Short.valueOf(Short.MIN_VALUE),
+ Integer.valueOf(Short.MAX_VALUE), Long.valueOf(82007160L)}) {
+ Integer i = NumberUtils.toInteger(n);
+ if (n instanceof Integer) {
+ assertSame("Unexpected conversion", n, i);
+ } else {
+ assertEquals("Mismatched values", n.intValue(), i.intValue());
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
new file mode 100644
index 0000000..af78a11
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.Objects;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class OsUtilsTest extends JUnitTestSupport {
+ public OsUtilsTest() {
+ super();
+ }
+
+ @Test
+ public void testSetOsTypeByProperty() {
+ try {
+ for (String osType : new String[]{"Some-Windows", "Some-Linux"}) {
+ OsUtils.setWin32(null); // force re-detection
+
+ try {
+ boolean expected = osType.contains("Windows");
+ System.setProperty(OsUtils.OS_TYPE_OVERRIDE_PROP, osType);
+ boolean actual = OsUtils.isWin32();
+ assertEquals(osType, expected, actual);
+ } finally {
+ System.clearProperty(OsUtils.OS_TYPE_OVERRIDE_PROP);
+ }
+ }
+ } finally {
+ OsUtils.setWin32(null); // force re-detection
+ }
+ }
+
+ @Test
+ public void testSetOsTypeProgrammatically() {
+ try {
+ for (boolean expected : new boolean[]{true, false}) {
+ OsUtils.setWin32(expected); // force value
+ assertEquals("Mismatched detection value", expected, OsUtils.isWin32());
+ }
+ } finally {
+ OsUtils.setWin32(null); // force re-detection
+ }
+ }
+
+ @Test
+ public void testSetCurrentUserByProperty() {
+ try {
+ for (String expected : new String[]{getClass().getSimpleName(), getCurrentTestName()}) {
+ OsUtils.setCurrentUser(null); // force re-detection
+
+ try {
+ System.setProperty(OsUtils.CURRENT_USER_OVERRIDE_PROP, expected);
+ String actual = OsUtils.getCurrentUser();
+ assertEquals("Mismatched reported current user", expected, actual);
+ } finally {
+ System.clearProperty(OsUtils.CURRENT_USER_OVERRIDE_PROP);
+ }
+ }
+ } finally {
+ OsUtils.setCurrentUser(null); // force re-detection
+ }
+ }
+
+ @Test
+ public void testSetCurrentUserProgrammatically() {
+ try {
+ for (String expected : new String[]{getClass().getSimpleName(), getCurrentTestName()}) {
+ OsUtils.setCurrentUser(expected); // force value
+ assertEquals("Mismatched detection value", expected, OsUtils.getCurrentUser());
+ }
+ } finally {
+ OsUtils.setCurrentUser(null); // force re-detection
+ }
+ }
+
+ @Test
+ public void testSetJavaVersionByProperty() {
+ try {
+ for (String value : new String[]{"7.3.6_5", "37.77.34_7-" + getCurrentTestName()}) {
+ OsUtils.setJavaVersion(null); // force re-detection
+
+ try {
+ System.setProperty(OsUtils.JAVA_VERSION_OVERRIDE_PROP, value);
+ String expected = value.replace('_', '.');
+ String actual = Objects.toString(OsUtils.getJavaVersion(), null);
+ assertTrue("Mismatched reported version value: " + actual, expected.startsWith(actual));
+ } finally {
+ System.clearProperty(OsUtils.JAVA_VERSION_OVERRIDE_PROP);
+ }
+ }
+ } finally {
+ OsUtils.setJavaVersion(null); // force re-detection
+ }
+ }
+
+ @Test
+ public void testSetJavaVersionProgrammatically() {
+ try {
+ for (VersionInfo expected : new VersionInfo[]{VersionInfo.parse("7.3.6.5"), VersionInfo.parse("37.77.34.7")}) {
+ OsUtils.setJavaVersion(expected); // force value
+ assertEquals("Mismatched detection value", expected, OsUtils.getJavaVersion());
+ }
+ } finally {
+ OsUtils.setJavaVersion(null); // force re-detection
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java
new file mode 100644
index 0000000..3044699
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.io.File;
+import java.util.Random;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class SelectorUtilsTest extends JUnitTestSupport {
+ public SelectorUtilsTest() {
+ super();
+ }
+
+ @Test
+ public void testApplyLinuxSeparatorSlashifyRules() {
+ testApplySlashifyRules('/');
+ }
+
+ @Test
+ public void testApplyWindowsSeparatorSlashifyRules() {
+ testApplySlashifyRules('\\');
+ }
+
+ private void testApplySlashifyRules(char slash) {
+ for (String expected : new String[]{
+ null, "", getCurrentTestName(),
+ getClass().getSimpleName() + Character.toString(slash) + getCurrentTestName(),
+ Character.toString(slash) + getClass().getSimpleName(),
+ Character.toString(slash) + getClass().getSimpleName() + Character.toString(slash) + getCurrentTestName()
+ }) {
+ String actual = SelectorUtils.applySlashifyRules(expected, slash);
+ assertSame("Mismatched results for '" + expected + "'", expected, actual);
+ }
+
+ String[] comps = {getClass().getSimpleName(), getCurrentTestName()};
+ Random rnd = new Random(System.nanoTime());
+ StringBuilder sb = new StringBuilder(Byte.MAX_VALUE);
+ for (int index = 0; index < Long.SIZE; index++) {
+ if (sb.length() > 0) {
+ sb.setLength(0); // start from scratch
+ }
+
+ boolean prepend = rnd.nextBoolean();
+ if (prepend) {
+ slashify(sb, rnd, slash);
+ }
+
+ sb.append(comps[0]);
+ for (int j = 1; j < comps.length; j++) {
+ slashify(sb, rnd, slash);
+ sb.append(comps[j]);
+ }
+
+ boolean append = rnd.nextBoolean();
+ if (append) {
+ slashify(sb, rnd, slash);
+ }
+
+ String path = sb.toString();
+ sb.setLength(0);
+ if (prepend) {
+ sb.append(slash);
+ }
+
+ sb.append(comps[0]);
+ for (int j = 1; j < comps.length; j++) {
+ sb.append(slash).append(comps[j]);
+ }
+
+ if (append) {
+ sb.append(slash).append('.');
+ }
+
+ String expected = sb.toString();
+ String actual = SelectorUtils.applySlashifyRules(path, slash);
+ assertEquals("Mismatched results for path=" + path, expected, actual);
+ }
+ }
+
+ private static int slashify(StringBuilder sb, Random rnd, char slash) {
+ int slashes = 1 /* at least one slash */ + rnd.nextInt(Byte.SIZE);
+ for (int k = 0; k < slashes; k++) {
+ sb.append(slash);
+ }
+
+ return slashes;
+ }
+
+ @Test
+ public void testTranslateToFileSystemPath() {
+ String path = getClass().getPackage().getName().replace('.', File.separatorChar)
+ + File.separator + getClass().getSimpleName()
+ + File.separator + getCurrentTestName();
+ for (String expected : new String[] {null, "", path}) {
+ String actual = SelectorUtils.translateToFileSystemPath(expected, File.separator, File.separator);
+ assertSame("Mismatched instance for translated result", expected, actual);
+ }
+
+ for (String fsSeparator : new String[] {String.valueOf('.'), "##"}) {
+ String expected = path.replace(File.separator, fsSeparator);
+ String actual = SelectorUtils.translateToFileSystemPath(path, File.separator, fsSeparator);
+ assertEquals("Mismatched translation result for separator='" + fsSeparator + "'", expected, actual);
+
+ actual = SelectorUtils.translateToFileSystemPath(actual, fsSeparator, File.separator);
+ assertEquals("Mismatched translation revert for separator='" + fsSeparator + "'", path, actual);
+ }
+ }
+
+ @Test
+ public void testAbsoluteWindowsPathTranslation() {
+ Assume.assumeTrue("Not tested on Windows", OsUtils.isWin32());
+ String expected = detectTargetFolder().toString();
+ for (String prefix : new String[]{"", "/"}) {
+ String actual = SelectorUtils.translateToLocalPath(prefix + expected.replace('/', File.separatorChar));
+ assertEquals("Mismatched result for prefix='" + prefix + "'", expected, actual);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java
new file mode 100644
index 0000000..10e6f5b
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.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.util;
+
+import java.util.Collection;
+
+import org.apache.sshd.common.util.threads.CloseableExecutorService;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class ThreadUtilsTest extends JUnitTestSupport {
+ public ThreadUtilsTest() {
+ super();
+ }
+
+ @Test
+ public void testProtectExecutorServiceShutdown() {
+ for (boolean shutdownOnExit : new boolean[]{true, false}) {
+ assertNull("Unexpected instance for shutdown=" + shutdownOnExit, ThreadUtils.protectExecutorServiceShutdown(null, shutdownOnExit));
+ }
+
+ CloseableExecutorService service = ThreadUtils.newSingleThreadExecutor("pool");
+ try {
+ assertSame("Unexpected wrapped instance", service, ThreadUtils.protectExecutorServiceShutdown(service, true));
+
+ CloseableExecutorService wrapped = ThreadUtils.protectExecutorServiceShutdown(service, false);
+ try {
+ assertNotSame("No wrapping occurred", service, wrapped);
+
+ wrapped.shutdown();
+ assertTrue("Wrapped service not shutdown", wrapped.isShutdown());
+ assertFalse("Protected service is shutdown", service.isShutdown());
+
+ Collection<?> running = wrapped.shutdownNow();
+ assertTrue("Non-empty runners list", running.isEmpty());
+ assertTrue("Wrapped service not shutdownNow", wrapped.isShutdown());
+ assertFalse("Protected service is shutdownNow", service.isShutdown());
+ } finally {
+ wrapped.shutdownNow(); // just in case
+ }
+ } finally {
+ service.shutdownNow(); // just in case...
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java
new file mode 100644
index 0000000..9efd5f9
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category(NoIoTestCase.class)
+public class ValidateUtilsTest extends JUnitTestSupport {
+ public ValidateUtilsTest() {
+ super();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void checkNotNull() {
+ ValidateUtils.checkNotNull(getClass(), getCurrentTestName());
+ ValidateUtils.checkNotNull(null, getCurrentTestName());
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java
new file mode 100644
index 0000000..834be24
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class VersionInfoTest extends JUnitTestSupport {
+ public VersionInfoTest() {
+ super();
+ }
+
+ @Test
+ public void testLessThan4Components() {
+ VersionInfo expected = new VersionInfo(73, 65);
+ VersionInfo actual = VersionInfo.parse(NumberUtils.join('.', expected.getMajorVersion(), expected.getMinorVersion()));
+ assertEquals("Mismatched result", expected, actual);
+ }
+
+ @Test
+ public void testMoreThan4Components() {
+ VersionInfo expected = new VersionInfo(7, 3, 6, 5);
+ VersionInfo actual = VersionInfo.parse(expected.toString() + ".3.7.7.7.3.4.7");
+ assertEquals("Mismatched result", expected, actual);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
new file mode 100644
index 0000000..6f41b76
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.buffer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BufferTest extends JUnitTestSupport {
+ public BufferTest() {
+ super();
+ }
+
+ @Test
+ public void testGetLong() throws Exception {
+ long expected = 1234567890123456789L;
+
+ try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
+ try (DataOutputStream ds = new DataOutputStream(stream)) {
+ ds.writeLong(expected);
+ }
+
+ Buffer buffer = new ByteArrayBuffer(stream.toByteArray());
+ assertEquals("Mismatched recovered value", expected, buffer.getLong());
+ }
+ }
+
+ @Test
+ public void testPutCharsWithNullOrEmptyValue() {
+ Buffer buffer = new ByteArrayBuffer(Integer.SIZE);
+ for (char[] chars : new char[][]{null, GenericUtils.EMPTY_CHAR_ARRAY}) {
+ buffer.putChars(chars);
+
+ String value = buffer.getString();
+ assertEquals("Mismatched value for " + ((chars == null) ? "null" : "empty") + " characters", "", value);
+ }
+ }
+
+ @Test
+ public void testPutCharsOnNonEmptyValue() {
+ String expected = getCurrentTestName();
+ Buffer buffer = new ByteArrayBuffer(expected.length() + Byte.SIZE);
+ buffer.putChars(expected.toCharArray());
+
+ String actual = buffer.getString();
+ assertEquals("Mismatched recovered values", expected, actual);
+ }
+
+ @Test
+ public void testPutAndWipeChars() {
+ String expected = getCurrentTestName();
+ char[] chars = expected.toCharArray();
+ Buffer buffer = new ByteArrayBuffer(chars.length + Byte.SIZE);
+ buffer.putAndWipeChars(chars);
+
+ String actual = buffer.getString();
+ assertEquals("Mismatched recovered values", expected, actual);
+
+ for (int index = 0; index < chars.length; index++) {
+ assertEquals("Character not wiped at index=" + index, 0, chars[index]);
+ }
+ }
+
+ @Test
+ public void testPutAndWipeBytes() {
+ String expected = getCurrentTestName();
+ byte[] bytes = expected.getBytes(StandardCharsets.UTF_8);
+ Buffer buffer = new ByteArrayBuffer(bytes.length + Byte.SIZE);
+ buffer.putAndWipeBytes(bytes);
+ String actual = buffer.getString();
+ assertEquals("Mismatched recovered values", expected, actual);
+
+ for (int index = 0; index < bytes.length; index++) {
+ assertEquals("Value not wiped at index=" + index, 0, bytes[index]);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
new file mode 100644
index 0000000..78122b7
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.buffer;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Random;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BufferUtilsTest extends JUnitTestSupport {
+ public BufferUtilsTest() {
+ super();
+ }
+
+ @Test
+ public void testHexEncodeDecode() {
+ String expValue = getClass().getName() + "#" + getCurrentTestName();
+ byte[] expData = expValue.getBytes(StandardCharsets.UTF_8);
+ for (char sep : new char[]{BufferUtils.EMPTY_HEX_SEPARATOR, ':'}) {
+ String hexData = BufferUtils.toHex(sep, expData);
+ byte[] actData = BufferUtils.decodeHex(sep, hexData);
+ String actValue = new String(actData, StandardCharsets.UTF_8);
+ String sepName = (BufferUtils.EMPTY_HEX_SEPARATOR == sep) ? "EMPTY" : Character.toString(sep);
+ outputDebugMessage("Decode(sep=%s) expected=%s, actual=%s", sepName, expValue, actValue);
+ assertArrayEquals("Mismatched result for sep='" + sepName + "'", expData, actData);
+ }
+ }
+
+ @Test
+ public void testGetCompactClone() {
+ byte[] expected = getCurrentTestName().getBytes(StandardCharsets.UTF_8);
+ final int testOffset = Byte.SIZE / 2;
+ byte[] data = new byte[expected.length + 2 * testOffset];
+ Random rnd = new Random(System.nanoTime());
+ rnd.nextBytes(data);
+ System.arraycopy(expected, 0, data, testOffset, expected.length);
+
+ Buffer buf = ByteArrayBuffer.getCompactClone(data, testOffset, expected.length);
+ assertEquals("Mismatched cloned buffer read position", 0, buf.rpos());
+ assertEquals("Mismatched cloned buffer available size", expected.length, buf.available());
+
+ byte[] actual = buf.array();
+ assertNotSame("Original data not cloned", data, actual);
+ assertArrayEquals("Mismatched cloned contents", expected, actual);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
new file mode 100644
index 0000000..6ed1187
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.closeable;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class CloseableUtilsTest extends JUnitTestSupport {
+ public CloseableUtilsTest() {
+ super();
+ }
+
+ @Test
+ public void testCloseImmediateNotCalledIfAlreadyClosed() throws IOException {
+ Closeable closeable = new IoBaseCloseable() {
+ @Override
+ public CloseFuture close(boolean immediately) {
+ fail("Unexpected call to close(" + immediately + ")");
+ return null;
+ }
+
+ @Override
+ public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ fail("Unexpected call to addCloseFutureListener");
+ }
+
+ @Override
+ public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ fail("Unexpected call to removeCloseFutureListener");
+ }
+
+ @Override
+ public boolean isClosed() {
+ return true;
+ }
+
+ @Override
+ public boolean isClosing() {
+ return false;
+ }
+ };
+ closeable.close();
+ }
+
+ @Test
+ public void testCloseImmediateNotCalledIfIsClosing() throws IOException {
+ Closeable closeable = new IoBaseCloseable() {
+ @Override
+ public CloseFuture close(boolean immediately) {
+ fail("Unexpected call to close(" + immediately + ")");
+ return null;
+ }
+
+ @Override
+ public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ fail("Unexpected call to addCloseFutureListener");
+ }
+
+ @Override
+ public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ fail("Unexpected call to removeCloseFutureListener");
+ }
+
+ @Override
+ public boolean isClosed() {
+ return false;
+ }
+
+ @Override
+ public boolean isClosing() {
+ return true;
+ }
+ };
+ closeable.close();
+ }
+
+ @Test
+ public void testCloseImmediateCalledAndWait() throws Exception {
+ DefaultCloseFuture future = new DefaultCloseFuture(this, this);
+ AtomicInteger callsCount = new AtomicInteger(0);
+ Closeable closeable = new IoBaseCloseable() {
+ @Override
+ public CloseFuture close(boolean immediately) {
+ assertTrue("Closure is not immediate", immediately);
+ assertEquals("Multiple close immediate calls", 1, callsCount.incrementAndGet());
+ return future;
+ }
+
+ @Override
+ public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ fail("Unexpected call to addCloseFutureListener");
+ }
+
+ @Override
+ public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ fail("Unexpected call to removeCloseFutureListener");
+ }
+
+ @Override
+ public boolean isClosed() {
+ return false;
+ }
+
+ @Override
+ public boolean isClosing() {
+ return false;
+ }
+ };
+
+ ExecutorService service = ThreadUtils.newSingleThreadExecutor(getCurrentTestName());
+ try {
+ Future<?> task = service.submit((Runnable) () -> {
+ try {
+ closeable.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ future.setClosed(); // signal close complete
+ task.get(5L, TimeUnit.SECONDS); // make sure #await call terminated
+ assertEquals("Close immediate not called", 1, callsCount.get());
+ } finally {
+ service.shutdownNow();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
new file mode 100644
index 0000000..113758d
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class EmptyInputStreamTest extends JUnitTestSupport {
+ public EmptyInputStreamTest() {
+ super();
+ }
+
+ @Test
+ public void testEmptyInputStream() throws IOException {
+ try (EmptyInputStream in = new EmptyInputStream()) {
+ testEmptyInputStream(in, false);
+ }
+ }
+
+ @Test
+ public void testCloseableEmptyInputStream() throws IOException {
+ try (EmptyInputStream in = new CloseableEmptyInputStream()) {
+ testEmptyInputStream(in, true);
+ }
+ }
+
+ private void testEmptyInputStream(InputStream in, boolean failAfterClose) throws IOException {
+ testEmptyInputStream("open", in, false);
+ in.close();
+ testEmptyInputStream("closed", in, failAfterClose);
+ }
+
+ private void testEmptyInputStream(String message, InputStream in, boolean errorExpected) {
+ assertFalse(message + ": unexpected markSupported()", in.markSupported());
+ try {
+ in.mark(Long.SIZE);
+ fail(message + ": unexpected mark success");
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+
+ try {
+ int len = in.available();
+ assertFalse(message + ": Unexpected success in available(): " + len, errorExpected);
+ assertEquals(message + ": Mismatched available() result", 0, len);
+ } catch (IOException e) {
+ assertTrue(message + ": Unexpected error on available(): " + e.getMessage(), errorExpected);
+ }
+
+ try {
+ int data = in.read();
+ assertFalse(message + ": Unexpected success in read(): " + data, errorExpected);
+ assertEquals(message + ": Mismatched read() result", -1, data);
+ } catch (IOException e) {
+ assertTrue(message + ": Unexpected error on read(): " + e.getMessage(), errorExpected);
+ }
+
+ byte[] bytes = new byte[Byte.SIZE];
+ try {
+ int len = in.read(bytes);
+ assertFalse(message + ": Unexpected success in read([]): " + BufferUtils.toHex(':', bytes), errorExpected);
+ assertEquals(message + ": Mismatched read([]) result", -1, len);
+ } catch (IOException e) {
+ assertTrue(message + ": Unexpected error on read([]): " + e.getMessage(), errorExpected);
+ }
+
+ try {
+ int len = in.read(bytes, 0, bytes.length);
+ assertFalse(message + ": Unexpected success in read([],int,int): " + BufferUtils.toHex(':', bytes), errorExpected);
+ assertEquals(message + ": Mismatched read([],int,int) result", -1, len);
+ } catch (IOException e) {
+ assertTrue(message + ": Unexpected error on read([],int,int): " + e.getMessage(), errorExpected);
+ }
+
+ try {
+ long len = in.skip(Byte.MAX_VALUE);
+ assertFalse(message + ": Unexpected success in skip(): " + len, errorExpected);
+ assertEquals(message + ": Mismatched skip() result", 0L, len);
+ } catch (IOException e) {
+ assertTrue(message + ": Unexpected error on skip(): " + e.getMessage(), errorExpected);
+ }
+
+ try {
+ in.reset();
+ assertFalse(message + ": Unexpected success in reset()", errorExpected);
+ } catch (IOException e) {
+ assertTrue(message + ": Unexpected error on reset(): " + e.getMessage(), errorExpected);
+ }
+ }
+}
[42/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestUtils.java
new file mode 100644
index 0000000..30369ac
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/digest/DigestUtils.java
@@ -0,0 +1,228 @@
+/*
+ * 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.digest;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Objects;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class DigestUtils {
+ private DigestUtils() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ /**
+ * @param algorithm The digest algorithm - never {@code null}/empty
+ * @return {@code true} if this digest algorithm is supported
+ * @see SecurityUtils#getMessageDigest(String)
+ */
+ public static boolean checkSupported(String algorithm) {
+ ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm");
+ try {
+ MessageDigest digest = SecurityUtils.getMessageDigest(algorithm);
+ return digest != null; // just in case
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
+ * @param <D> The generic type of digest factory
+ * @param algo The required algorithm name - ignored if {@code null}/empty
+ * @param comp The {@link Comparator} to use to compare algorithm names
+ * @param digests The factories to check - ignored if {@code null}/empty
+ * @return The first {@link DigestFactory} whose algorithm matches the required one
+ * according to the comparator - {@code null} if no match found
+ */
+ public static <D extends Digest> D findDigestByAlgorithm(String algo, Comparator<? super String> comp, Collection<? extends D> digests) {
+ if (GenericUtils.isEmpty(algo) || GenericUtils.isEmpty(digests)) {
+ return null;
+ }
+
+ for (D d : digests) {
+ if (comp.compare(algo, d.getAlgorithm()) == 0) {
+ return d;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param <F> The generic type of digest factory
+ * @param algo The required algorithm name - ignored if {@code null}/empty
+ * @param comp The {@link Comparator} to use to compare algorithm names
+ * @param factories The factories to check - ignored if {@code null}/empty
+ * @return The first {@link DigestFactory} whose algorithm matches the required one
+ * according to the comparator - {@code null} if no match found
+ */
+ public static <F extends DigestFactory> F findFactoryByAlgorithm(String algo, Comparator<? super String> comp, Collection<? extends F> factories) {
+ if (GenericUtils.isEmpty(algo) || GenericUtils.isEmpty(factories)) {
+ return null;
+ }
+
+ for (F f : factories) {
+ if (comp.compare(algo, f.getAlgorithm()) == 0) {
+ return f;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param f The {@link Factory} to create the {@link Digest} to use
+ * @param s The {@link String} to digest - ignored if {@code null}/empty,
+ * otherwise its UTF-8 representation is used as input for the fingerprint
+ * @return The fingerprint - {@code null} if {@code null}/empty input
+ * @throws Exception If failed to calculate the digest
+ * @see #getFingerPrint(Digest, String, Charset)
+ */
+ public static String getFingerPrint(Factory<? extends Digest> f, String s) throws Exception {
+ return getFingerPrint(f, s, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * @param f The {@link Factory} to create the {@link Digest} to use
+ * @param s The {@link String} to digest - ignored if {@code null}/empty
+ * @param charset The {@link Charset} to use in order to convert the
+ * string to its byte representation to use as input for the fingerprint
+ * @return The fingerprint - {@code null} if {@code null}/empty input
+ * @throws Exception If failed to calculate the digest
+ */
+ public static String getFingerPrint(Factory<? extends Digest> f, String s, Charset charset) throws Exception {
+ return getFingerPrint(Objects.requireNonNull(f, "No factory").create(), s, charset);
+ }
+
+ /**
+ * @param d The {@link Digest} to use
+ * @param s The {@link String} to digest - ignored if {@code null}/empty,
+ * otherwise its UTF-8 representation is used as input for the fingerprint
+ * @return The fingerprint - {@code null} if {@code null}/empty input
+ * @throws Exception If failed to calculate the digest
+ * @see #getFingerPrint(Digest, String, Charset)
+ */
+ public static String getFingerPrint(Digest d, String s) throws Exception {
+ return getFingerPrint(d, s, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * @param d The {@link Digest} to use
+ * @param s The {@link String} to digest - ignored if {@code null}/empty
+ * @param charset The {@link Charset} to use in order to convert the
+ * string to its byte representation to use as input for the fingerprint
+ * @return The fingerprint - {@code null} if {@code null}/empty input
+ * @throws Exception If failed to calculate the digest
+ */
+ public static String getFingerPrint(Digest d, String s, Charset charset) throws Exception {
+ if (GenericUtils.isEmpty(s)) {
+ return null;
+ } else {
+ return DigestUtils.getFingerPrint(d, s.getBytes(charset));
+ }
+ }
+
+ /**
+ * @param f The {@link Factory} to create the {@link Digest} to use
+ * @param buf The data buffer to be fingerprint-ed
+ * @return The fingerprint - {@code null} if empty data buffer
+ * @throws Exception If failed to calculate the fingerprint
+ * @see #getFingerPrint(Factory, byte[], int, int)
+ */
+ public static String getFingerPrint(Factory<? extends Digest> f, byte... buf) throws Exception {
+ return getFingerPrint(f, buf, 0, NumberUtils.length(buf));
+ }
+
+ /**
+ * @param f The {@link Factory} to create the {@link Digest} to use
+ * @param buf The data buffer to be fingerprint-ed
+ * @param offset The offset of the data in the buffer
+ * @param len The length of data - ignored if non-positive
+ * @return The fingerprint - {@code null} if non-positive length
+ * @throws Exception If failed to calculate the fingerprint
+ */
+ public static String getFingerPrint(Factory<? extends Digest> f, byte[] buf, int offset, int len) throws Exception {
+ return getFingerPrint(Objects.requireNonNull(f, "No factory").create(), buf, offset, len);
+ }
+
+ /**
+ * @param d The {@link Digest} to use
+ * @param buf The data buffer to be fingerprint-ed
+ * @return The fingerprint - {@code null} if empty data buffer
+ * @throws Exception If failed to calculate the fingerprint
+ * @see #getFingerPrint(Digest, byte[], int, int)
+ */
+ public static String getFingerPrint(Digest d, byte... buf) throws Exception {
+ return getFingerPrint(d, buf, 0, NumberUtils.length(buf));
+ }
+
+ /**
+ * @param d The {@link Digest} to use
+ * @param buf The data buffer to be fingerprint-ed
+ * @param offset The offset of the data in the buffer
+ * @param len The length of data - ignored if non-positive
+ * @return The fingerprint - {@code null} if non-positive length
+ * @throws Exception If failed to calculate the fingerprint
+ * @see #getRawFingerprint(Digest, byte[], int, int)
+ */
+ public static String getFingerPrint(Digest d, byte[] buf, int offset, int len) throws Exception {
+ if (len <= 0) {
+ return null;
+ }
+
+ byte[] data = getRawFingerprint(d, buf, offset, len);
+ String algo = d.getAlgorithm();
+ if (BuiltinDigests.md5.getAlgorithm().equals(algo)) {
+ return algo + ":" + BufferUtils.toHex(':', data).toLowerCase();
+ }
+
+ Base64.Encoder encoder = Base64.getEncoder();
+ return algo.replace("-", "").toUpperCase() + ":" + encoder.encodeToString(data).replaceAll("=", "");
+ }
+
+ public static byte[] getRawFingerprint(Digest d, byte... buf) throws Exception {
+ return getRawFingerprint(d, buf, 0, NumberUtils.length(buf));
+ }
+
+ public static byte[] getRawFingerprint(Digest d, byte[] buf, int offset, int len) throws Exception {
+ if (len <= 0) {
+ return null;
+ }
+
+ Objects.requireNonNull(d, "No digest").init();
+ d.update(buf, offset, len);
+
+ return d.digest();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/digest/package.html
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/digest/package.html b/sshd-common/src/main/java/org/apache/sshd/common/digest/package.html
new file mode 100644
index 0000000..65940e0
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/digest/package.html
@@ -0,0 +1,25 @@
+<!--
+ 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.
+-->
+<html>
+<head>
+</head>
+<body>
+
+<a href="{@docRoot}/org/apache/sshd/common/digest/Digest.html"><code>Digest</code></a> implementations.
+
+</body>
+</html>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java b/sshd-common/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java
new file mode 100644
index 0000000..1dca54c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java
@@ -0,0 +1,194 @@
+/*
+ * 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.future;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.StreamCorruptedException;
+import java.util.function.Function;
+
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * @param <T> Type of future
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractSshFuture<T extends SshFuture> extends AbstractLoggingBean implements SshFuture<T> {
+ /**
+ * A default value to indicate the future has been canceled
+ */
+ protected static final Object CANCELED = new Object();
+
+ protected final boolean debugEnabled;
+ protected final boolean traceEnabled;
+ private final Object id;
+
+ /**
+ * @param id Some identifier useful as {@link #toString()} value
+ */
+ protected AbstractSshFuture(Object id) {
+ this.id = id;
+ this.debugEnabled = log.isDebugEnabled();
+ this.traceEnabled = log.isTraceEnabled();
+ }
+
+ @Override
+ public Object getId() {
+ return id;
+ }
+
+ @Override
+ public boolean await(long timeoutMillis) throws IOException {
+ return await0(timeoutMillis, true) != null;
+ }
+
+ @Override
+ public boolean awaitUninterruptibly(long timeoutMillis) {
+ try {
+ return await0(timeoutMillis, false) != null;
+ } catch (InterruptedIOException e) {
+ throw formatExceptionMessage(msg -> new InternalError(msg, e),
+ "Unexpected interrupted exception wile awaitUninterruptibly %d msec: %s",
+ timeoutMillis, e.getMessage());
+ }
+ }
+
+ /**
+ * <P>Waits (interruptible) for the specified timeout (msec.) and then checks
+ * the result:</P>
+ * <UL>
+ * <LI><P>
+ * If result is {@code null} then timeout is assumed to have expired - throw
+ * an appropriate {@link IOException}
+ * </P></LI>
+ *
+ * <LI><P>
+ * If the result is of the expected type, then cast and return it
+ * </P></LI>
+ *
+ * <LI><P>
+ * If the result is an {@link IOException} then re-throw it
+ * </P></LI>
+ *
+ * <LI><P>
+ * If the result is a {@link Throwable} then throw an {@link IOException}
+ * whose cause is the original exception
+ * </P></LI>
+ *
+ * <LI><P>
+ * Otherwise (should never happen), throw a {@link StreamCorruptedException}
+ * with the name of the result type
+ * </P></LI>
+ * </UL>
+ *
+ * @param <R> The generic result type
+ * @param expectedType The expected result type
+ * @param timeout The timeout (millis) to wait for a result
+ * @return The (never {@code null}) result
+ * @throws IOException If failed to retrieve the expected result on time
+ */
+ protected <R> R verifyResult(Class<? extends R> expectedType, long timeout) throws IOException {
+ Object value = await0(timeout, true);
+ if (value == null) {
+ throw formatExceptionMessage(SshException::new, "Failed to get operation result within specified timeout: %s", timeout);
+ }
+
+ Class<?> actualType = value.getClass();
+ if (expectedType.isAssignableFrom(actualType)) {
+ return expectedType.cast(value);
+ }
+
+ if (Throwable.class.isAssignableFrom(actualType)) {
+ Throwable t = GenericUtils.peelException((Throwable) value);
+ if (t != value) {
+ value = t;
+ actualType = value.getClass();
+ }
+
+ if (IOException.class.isAssignableFrom(actualType)) {
+ throw (IOException) value;
+ }
+
+ Throwable cause = GenericUtils.resolveExceptionCause(t);
+ throw formatExceptionMessage(msg -> new SshException(msg, cause), "Failed (%s) to execute: %s", t.getClass().getSimpleName(), t.getMessage());
+ } else { // what else can it be ????
+ throw formatExceptionMessage(StreamCorruptedException::new, "Unknown result type: %s", actualType.getName());
+ }
+ }
+
+ /**
+ * Wait for the Future to be ready. If the requested delay is 0 or
+ * negative, this method returns immediately.
+ *
+ * @param timeoutMillis The delay we will wait for the Future to be ready
+ * @param interruptable Tells if the wait can be interrupted or not.
+ * If {@code true} and the thread is interrupted then an {@link InterruptedIOException}
+ * is thrown.
+ * @return The non-{@code null} result object if the Future is ready,
+ * {@code null} if the timeout expired and no result was received
+ * @throws InterruptedIOException If the thread has been interrupted when it's not allowed.
+ */
+ protected abstract Object await0(long timeoutMillis, boolean interruptable) throws InterruptedIOException;
+
+ @SuppressWarnings("unchecked")
+ protected SshFutureListener<T> asListener(Object o) {
+ return (SshFutureListener<T>) o;
+ }
+
+ protected void notifyListener(SshFutureListener<T> l) {
+ try {
+ l.operationComplete(asT());
+ } catch (Throwable t) {
+ log.warn("notifyListener({}) failed ({}) to invoke {}: {}",
+ this, t.getClass().getSimpleName(), l, t.getMessage());
+ if (debugEnabled) {
+ log.debug("notifyListener(" + this + ")[" + l + "] invocation failure details", t);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected T asT() {
+ return (T) this;
+ }
+
+ /**
+ * Generates an exception whose message is prefixed by the future simple class name + {@link #getId() identifier}
+ * as a hint to the context of the failure.
+ *
+ * @param <E> Type of {@link Throwable} being generated
+ * @param exceptionCreator The exception creator from the formatted message
+ * @param format The extra payload format as per {@link String#format(String, Object...)}
+ * @param args The formatting arguments
+ * @return The generated exception
+ */
+ protected <E extends Throwable> E formatExceptionMessage(Function<? super String, ? extends E> exceptionCreator, String format, Object... args) {
+ String messagePayload = String.format(format, args);
+ String excMessage = getClass().getSimpleName() + "[" + getId() + "]: " + messagePayload;
+ return exceptionCreator.apply(excMessage);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[id=" + getId() + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/CloseFuture.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/CloseFuture.java b/sshd-common/src/main/java/org/apache/sshd/common/future/CloseFuture.java
new file mode 100644
index 0000000..808716d
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/CloseFuture.java
@@ -0,0 +1,40 @@
+/*
+ * 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.future;
+
+/**
+ * An {@link SshFuture} for asynchronous close requests.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface CloseFuture extends SshFuture<CloseFuture> {
+
+ /**
+ * @return <tt>true</tt> if the close request is finished and the target is closed.
+ */
+ boolean isClosed();
+
+ /**
+ * Marks this future as closed and notifies all threads waiting for this
+ * future. This method is invoked by SSHD internally. Please do not call
+ * this method directly.
+ */
+ void setClosed();
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java b/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java
new file mode 100644
index 0000000..4c34a06
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java
@@ -0,0 +1,52 @@
+/*
+ * 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.future;
+
+/**
+ * A default implementation of {@link CloseFuture}.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DefaultCloseFuture extends DefaultSshFuture<CloseFuture> implements CloseFuture {
+
+ /**
+ * Create a new instance
+ *
+ * @param id Some identifier useful as {@link #toString()} value
+ * @param lock A synchronization object for locking access - if {@code null}
+ * then synchronization occurs on {@code this} instance
+ */
+ public DefaultCloseFuture(Object id, Object lock) {
+ super(id, lock);
+ }
+
+ @Override
+ public boolean isClosed() {
+ if (isDone()) {
+ return (Boolean) getValue();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void setClosed() {
+ setValue(Boolean.TRUE);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java b/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
new file mode 100644
index 0000000..cd475ff
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
@@ -0,0 +1,236 @@
+/*
+ * 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.future;
+
+import java.io.InterruptedIOException;
+import java.lang.reflect.Array;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * A default implementation of {@link SshFuture}.
+ *
+ * @param <T> Type of future
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DefaultSshFuture<T extends SshFuture> extends AbstractSshFuture<T> {
+ /**
+ * A lock used by the wait() method
+ */
+ private final Object lock;
+ private Object listeners;
+ private Object result;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param id Some identifier useful as {@link #toString()} value
+ * @param lock A synchronization object for locking access - if {@code null}
+ * then synchronization occurs on {@code this} instance
+ */
+ public DefaultSshFuture(Object id, Object lock) {
+ super(id);
+
+ this.lock = (lock != null) ? lock : this;
+ }
+
+ @Override
+ protected Object await0(long timeoutMillis, boolean interruptable) throws InterruptedIOException {
+ ValidateUtils.checkTrue(timeoutMillis >= 0L, "Negative timeout N/A: %d", timeoutMillis);
+ long startTime = System.currentTimeMillis();
+ long curTime = startTime;
+ long endTime = ((Long.MAX_VALUE - timeoutMillis) < curTime) ? Long.MAX_VALUE : (curTime + timeoutMillis);
+
+ synchronized (lock) {
+ if ((result != null) || (timeoutMillis <= 0)) {
+ return result;
+ }
+
+ for (;;) {
+ try {
+ lock.wait(endTime - curTime);
+ } catch (InterruptedException e) {
+ if (interruptable) {
+ curTime = System.currentTimeMillis();
+ throw formatExceptionMessage(msg -> {
+ InterruptedIOException exc = new InterruptedIOException(msg);
+ exc.initCause(e);
+ return exc;
+ }, "Interrupted after %d msec.", curTime - startTime);
+ }
+ }
+
+ curTime = System.currentTimeMillis();
+ if ((result != null) || (curTime >= endTime)) {
+ return result;
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isDone() {
+ synchronized (lock) {
+ return result != null;
+ }
+ }
+
+ /**
+ * Sets the result of the asynchronous operation, and mark it as finished.
+ *
+ * @param newValue The operation result
+ */
+ public void setValue(Object newValue) {
+ synchronized (lock) {
+ // Allow only once.
+ if (result != null) {
+ return;
+ }
+
+ result = (newValue != null) ? newValue : GenericUtils.NULL;
+ lock.notifyAll();
+ }
+
+ notifyListeners();
+ }
+
+ public int getNumRegisteredListeners() {
+ synchronized (lock) {
+ if (listeners == null) {
+ return 0;
+ } else if (listeners instanceof SshFutureListener) {
+ return 1;
+ } else {
+ int l = Array.getLength(listeners);
+ int count = 0;
+ for (int i = 0; i < l; i++) {
+ if (Array.get(listeners, i) != null) {
+ count++;
+ }
+ }
+ return count;
+ }
+ }
+ }
+
+ /**
+ * @return The result of the asynchronous operation - or {@code null}
+ * if none set.
+ */
+ public Object getValue() {
+ synchronized (lock) {
+ return (result == GenericUtils.NULL) ? null : result;
+ }
+ }
+
+ @Override
+ public T addListener(SshFutureListener<T> listener) {
+ Objects.requireNonNull(listener, "Missing listener argument");
+ boolean notifyNow = false;
+ synchronized (lock) {
+ // if already have a result don't register the listener and invoke it directly
+ if (result != null) {
+ notifyNow = true;
+ } else if (listeners == null) {
+ listeners = listener; // 1st listener ?
+ } else if (listeners instanceof SshFutureListener) {
+ listeners = new Object[]{listeners, listener};
+ } else { // increase array of registered listeners
+ Object[] ol = (Object[]) listeners;
+ int l = ol.length;
+ Object[] nl = new Object[l + 1];
+ System.arraycopy(ol, 0, nl, 0, l);
+ nl[l] = listener;
+ listeners = nl;
+ }
+ }
+
+ if (notifyNow) {
+ notifyListener(listener);
+ }
+
+ return asT();
+ }
+
+ @Override
+ public T removeListener(SshFutureListener<T> listener) {
+ Objects.requireNonNull(listener, "No listener provided");
+
+ synchronized (lock) {
+ if (result != null) {
+ return asT(); // the train has already left the station...
+ }
+
+ if (listeners == null) {
+ return asT(); // no registered instances anyway
+ }
+
+ if (listeners == listener) {
+ listeners = null; // the one and only
+ } else if (!(listeners instanceof SshFutureListener)) {
+ int l = Array.getLength(listeners);
+ for (int i = 0; i < l; i++) {
+ if (Array.get(listeners, i) == listener) {
+ Array.set(listeners, i, null);
+ break;
+ }
+ }
+ }
+ }
+
+ return asT();
+ }
+
+ protected void notifyListeners() {
+ /*
+ * There won't be any visibility problem or concurrent modification
+ * because result value is checked in both addListener and
+ * removeListener calls under lock. If the result is already set then
+ * both methods will not modify the internal listeners
+ */
+ if (listeners != null) {
+ if (listeners instanceof SshFutureListener) {
+ notifyListener(asListener(listeners));
+ } else {
+ int l = Array.getLength(listeners);
+ for (int i = 0; i < l; i++) {
+ SshFutureListener<T> listener = asListener(Array.get(listeners, i));
+ if (listener != null) {
+ notifyListener(listener);
+ }
+ }
+ }
+ }
+ }
+
+ public boolean isCanceled() {
+ return getValue() == CANCELED;
+ }
+
+ public void cancel() {
+ setValue(CANCELED);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "[value=" + result + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultVerifiableSshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultVerifiableSshFuture.java b/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultVerifiableSshFuture.java
new file mode 100644
index 0000000..1b02fed
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/DefaultVerifiableSshFuture.java
@@ -0,0 +1,30 @@
+/*
+ * 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.future;
+
+/**
+ * @param <T> Type of future
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class DefaultVerifiableSshFuture<T extends SshFuture> extends DefaultSshFuture<T> implements VerifiableFuture<T> {
+ protected DefaultVerifiableSshFuture(Object id, Object lock) {
+ super(id, lock);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/SshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/SshFuture.java b/sshd-common/src/main/java/org/apache/sshd/common/future/SshFuture.java
new file mode 100644
index 0000000..b0d8e3c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/SshFuture.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.future;
+
+/**
+ * Represents the completion of an asynchronous SSH operation on a given object
+ * (it may be an SSH session or an SSH channel).
+ * Can be listened for completion using a {@link SshFutureListener}.
+ *
+ * @param <T> Type of future
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SshFuture<T extends SshFuture> extends WaitableFuture {
+ /**
+ * Adds an event <tt>listener</tt> which is notified when
+ * this future is completed. If the listener is added
+ * after the completion, the listener is directly notified.
+ *
+ * @param listener The {@link SshFutureListener} instance to add
+ * @return The future instance
+ */
+ T addListener(SshFutureListener<T> listener);
+
+ /**
+ * Removes an existing event <tt>listener</tt> so it won't be notified when
+ * the future is completed.
+ *
+ * @param listener The {@link SshFutureListener} instance to remove
+ * @return The future instance
+ */
+ T removeListener(SshFutureListener<T> listener);
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/SshFutureListener.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/SshFutureListener.java b/sshd-common/src/main/java/org/apache/sshd/common/future/SshFutureListener.java
new file mode 100644
index 0000000..a005c0f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/SshFutureListener.java
@@ -0,0 +1,42 @@
+/*
+ * 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.future;
+
+import org.apache.sshd.common.util.SshdEventListener;
+
+/**
+ * Something interested in being notified when the completion
+ * of an asynchronous SSH operation : {@link SshFuture}.
+ *
+ * @param <T> type of future
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@SuppressWarnings("rawtypes")
+@FunctionalInterface
+public interface SshFutureListener<T extends SshFuture> extends SshdEventListener {
+
+ /**
+ * Invoked when the operation associated with the {@link SshFuture}
+ * has been completed even if you add the listener after the completion.
+ *
+ * @param future The source {@link SshFuture} which called this
+ * callback.
+ */
+ void operationComplete(T future);
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/VerifiableFuture.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/VerifiableFuture.java b/sshd-common/src/main/java/org/apache/sshd/common/future/VerifiableFuture.java
new file mode 100644
index 0000000..e318de7
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/VerifiableFuture.java
@@ -0,0 +1,68 @@
+/*
+ * 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.future;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Represents an asynchronous operation whose successful result can be
+ * verified somehow. The contract guarantees that if the {@code verifyXXX}
+ * method returns without an exception then the operation was completed
+ * <U>successfully</U>
+ *
+ * @param <T> Type of verification result
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface VerifiableFuture<T> {
+ /**
+ * Wait {@link Long#MAX_VALUE} msec. and verify that the operation was successful
+ *
+ * @return The (same) future instance
+ * @throws IOException If failed to verify successfully on time
+ * @see #verify(long)
+ */
+ default T verify() throws IOException {
+ return verify(Long.MAX_VALUE);
+ }
+
+ /**
+ * Wait and verify that the operation was successful
+ *
+ * @param timeout The number of time units to wait
+ * @param unit The wait {@link TimeUnit}
+ * @return The (same) future instance
+ * @throws IOException If failed to verify successfully on time
+ * @see #verify(long)
+ */
+ default T verify(long timeout, TimeUnit unit) throws IOException {
+ return verify(unit.toMillis(timeout));
+ }
+
+ /**
+ * Wait and verify that the operation was successful
+ *
+ * @param timeoutMillis Wait timeout in milliseconds
+ * @return The (same) future instance
+ * @throws IOException If failed to verify successfully on time
+ */
+ T verify(long timeoutMillis) throws IOException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/future/WaitableFuture.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/future/WaitableFuture.java b/sshd-common/src/main/java/org/apache/sshd/common/future/WaitableFuture.java
new file mode 100644
index 0000000..aff4adc
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/future/WaitableFuture.java
@@ -0,0 +1,118 @@
+/*
+ * 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.future;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Represents an asynchronous operation which one can wait for its completion.
+ * <B>Note:</B> the only thing guaranteed is that if {@code true} is returned
+ * from one of the {@code awaitXXX} methods then the operation has completed.
+ * However, the <B>caller</B> has to determine whether it was a successful or
+ * failed completion.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface WaitableFuture {
+ /**
+ * @return Some identifier useful as {@link #toString()} value
+ */
+ Object getId();
+
+ /**
+ * Wait {@link Long#MAX_VALUE} msec. for the asynchronous operation to complete.
+ * The attached listeners will be notified when the operation is
+ * completed.
+ *
+ * @return {@code true} if the operation is completed.
+ * @throws IOException if failed - specifically {@link java.io.InterruptedIOException}
+ * if waiting was interrupted
+ * @see #await(long)
+ */
+ default boolean await() throws IOException {
+ return await(Long.MAX_VALUE);
+ }
+
+ /**
+ * Wait for the asynchronous operation to complete with the specified timeout.
+ *
+ * @param timeout The number of time units to wait
+ * @param unit The {@link TimeUnit} for waiting
+ * @return {@code true} if the operation is completed.
+ * @throws IOException if failed - specifically {@link java.io.InterruptedIOException}
+ * if waiting was interrupted
+ * @see #await(long)
+ */
+ default boolean await(long timeout, TimeUnit unit) throws IOException {
+ return await(unit.toMillis(timeout));
+ }
+
+ /**
+ * Wait for the asynchronous operation to complete with the specified timeout.
+ *
+ * @param timeoutMillis Wait time in milliseconds
+ * @return {@code true} if the operation is completed.
+ * @throws IOException if failed - specifically {@link java.io.InterruptedIOException}
+ * if waiting was interrupted
+ */
+ boolean await(long timeoutMillis) throws IOException;
+
+ /**
+ * Wait {@link Long#MAX_VALUE} msec. for the asynchronous operation to complete
+ * uninterruptibly. The attached listeners will be notified when the operation is
+ * completed.
+ *
+ * @return {@code true} if the operation is completed.
+ * @see #awaitUninterruptibly(long)
+ */
+ default boolean awaitUninterruptibly() {
+ return awaitUninterruptibly(Long.MAX_VALUE);
+ }
+
+ /**
+ * Wait for the asynchronous operation to complete with the specified timeout
+ * uninterruptibly.
+ *
+ * @param timeout The number of time units to wait
+ * @param unit The {@link TimeUnit} for waiting
+ * @return {@code true} if the operation is completed.
+ * @see #awaitUninterruptibly(long)
+ */
+ default boolean awaitUninterruptibly(long timeout, TimeUnit unit) {
+ return awaitUninterruptibly(unit.toMillis(timeout));
+ }
+
+ /**
+ * Wait for the asynchronous operation to complete with the specified timeout
+ * uninterruptibly.
+ *
+ * @param timeoutMillis Wait time in milliseconds
+ * @return {@code true} if the operation is finished.
+ */
+ boolean awaitUninterruptibly(long timeoutMillis);
+
+ /**
+ * @return {@code true} if the asynchronous operation is completed. <B>Note:</B>
+ * it is up to the <B>caller</B> to determine whether it was a successful or
+ * failed completion.
+ */
+ boolean isDone();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java
new file mode 100644
index 0000000..077d199
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.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.keyprovider;
+
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * Provides a default implementation for some {@link KeyPairProvider} methods
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractKeyPairProvider extends AbstractLoggingBean implements KeyPairProvider {
+ protected AbstractKeyPairProvider() {
+ super();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
new file mode 100644
index 0000000..e4e941d
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
@@ -0,0 +1,234 @@
+/*
+ * 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.keyprovider;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @param <R> Type of resource from which the {@link KeyPair} is generated
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPairProvider {
+ private FilePasswordProvider passwordFinder;
+ /*
+ * NOTE: the map is case insensitive even for Linux, as it is (very) bad
+ * practice to have 2 key files that differ from one another only in their
+ * case...
+ */
+ private final Map<String, KeyPair> cacheMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+ protected AbstractResourceKeyPairProvider() {
+ super();
+ }
+
+ public FilePasswordProvider getPasswordFinder() {
+ return passwordFinder;
+ }
+
+ public void setPasswordFinder(FilePasswordProvider passwordFinder) {
+ this.passwordFinder = passwordFinder;
+ }
+
+ /**
+ * Checks which of the new resources we already loaded and can keep the
+ * associated key pair
+ *
+ * @param resources The collection of new resources - can be {@code null}/empty
+ * in which case the cache is cleared
+ */
+ protected void resetCacheMap(Collection<?> resources) {
+ // if have any cached pairs then see what we can keep from previous load
+ Collection<String> toDelete = Collections.emptySet();
+ synchronized (cacheMap) {
+ if (cacheMap.size() <= 0) {
+ return; // already empty - nothing to keep
+ }
+
+ if (GenericUtils.isEmpty(resources)) {
+ cacheMap.clear();
+ return;
+ }
+
+ for (Object r : resources) {
+ String resourceKey = ValidateUtils.checkNotNullAndNotEmpty(Objects.toString(r, null), "No resource key value");
+ if (cacheMap.containsKey(resourceKey)) {
+ continue;
+ }
+
+ if (toDelete.isEmpty()) {
+ toDelete = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+ }
+
+ if (!toDelete.add(resourceKey)) {
+ continue; // debug breakpoint
+ }
+ }
+
+ if (GenericUtils.size(toDelete) > 0) {
+ toDelete.forEach(cacheMap::remove);
+ }
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("resetCacheMap(" + resources + ") removed previous cached keys for " + toDelete);
+ }
+ }
+
+ protected Iterable<KeyPair> loadKeys(final Collection<? extends R> resources) {
+ if (GenericUtils.isEmpty(resources)) {
+ return Collections.emptyList();
+ } else {
+ return () -> new KeyPairIterator(resources);
+ }
+ }
+
+ protected KeyPair doLoadKey(R resource) throws IOException, GeneralSecurityException {
+ String resourceKey = ValidateUtils.checkNotNullAndNotEmpty(Objects.toString(resource, null), "No resource string value");
+ KeyPair kp;
+ synchronized (cacheMap) {
+ // check if lucky enough to have already loaded this file
+ kp = cacheMap.get(resourceKey);
+ }
+
+ if (kp != null) {
+ if (log.isTraceEnabled()) {
+ PublicKey key = kp.getPublic();
+ log.trace("doLoadKey({}) use cached key {}-{}",
+ resourceKey, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+ }
+ return kp;
+ }
+
+ kp = doLoadKey(resourceKey, resource, getPasswordFinder());
+ if (kp != null) {
+ boolean reusedKey;
+ synchronized (cacheMap) {
+ // if somebody else beat us to it, use the cached key - just in case file contents changed
+ reusedKey = cacheMap.containsKey(resourceKey);
+ if (reusedKey) {
+ kp = cacheMap.get(resourceKey);
+ } else {
+ cacheMap.put(resourceKey, kp);
+ }
+ }
+
+ if (log.isDebugEnabled()) {
+ PublicKey key = kp.getPublic();
+ log.debug("doLoadKey({}) {} {}-{}",
+ resourceKey, reusedKey ? "re-loaded" : "loaded",
+ KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+ }
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("doLoadKey({}) no key loaded", resourceKey);
+ }
+ }
+
+ return kp;
+ }
+
+ protected KeyPair doLoadKey(String resourceKey, R resource, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
+ try (InputStream inputStream = openKeyPairResource(resourceKey, resource)) {
+ return doLoadKey(resourceKey, inputStream, provider);
+ }
+ }
+
+ protected abstract InputStream openKeyPairResource(String resourceKey, R resource) throws IOException;
+
+ protected KeyPair doLoadKey(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
+ throws IOException, GeneralSecurityException {
+ return SecurityUtils.loadKeyPairIdentity(resourceKey, inputStream, provider);
+ }
+
+ protected class KeyPairIterator implements Iterator<KeyPair> {
+ private final Iterator<? extends R> iterator;
+ private KeyPair nextKeyPair;
+ private boolean nextKeyPairSet;
+
+ protected KeyPairIterator(Collection<? extends R> resources) {
+ iterator = resources.iterator();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return nextKeyPairSet || setNextObject();
+ }
+
+ @Override
+ public KeyPair next() {
+ if (!nextKeyPairSet) {
+ if (!setNextObject()) {
+ throw new NoSuchElementException("Out of files to try");
+ }
+ }
+ nextKeyPairSet = false;
+ return nextKeyPair;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("loadKeys(files) Iterator#remove() N/A");
+ }
+
+ @SuppressWarnings("synthetic-access")
+ private boolean setNextObject() {
+ boolean debugEnabled = log.isDebugEnabled();
+ while (iterator.hasNext()) {
+ R r = iterator.next();
+ try {
+ nextKeyPair = doLoadKey(r);
+ } catch (Throwable e) {
+ log.warn("Failed (" + e.getClass().getSimpleName() + ")"
+ + " to load key resource=" + r + ": " + e.getMessage());
+ if (debugEnabled) {
+ log.debug("Key resource=" + r + " load failure details", e);
+ }
+ nextKeyPair = null;
+ continue;
+ }
+
+ if (nextKeyPair != null) {
+ nextKeyPairSet = true;
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java
new file mode 100644
index 0000000..6e8da54
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.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.keyprovider;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.security.KeyPair;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+
+/**
+ * This provider loads private keys from the specified resources that
+ * are accessible via {@link ClassLoader#getResourceAsStream(String)}.
+ * If no loader configured via {@link #setResourceLoader(ClassLoader)}, then
+ * {@link ThreadUtils#resolveDefaultClassLoader(Class)} is used
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ClassLoadableResourceKeyPairProvider extends AbstractResourceKeyPairProvider<String> {
+ private ClassLoader classLoader;
+ private Collection<String> resources;
+
+ public ClassLoadableResourceKeyPairProvider() {
+ this(Collections.emptyList());
+ }
+
+ public ClassLoadableResourceKeyPairProvider(ClassLoader cl) {
+ this(cl, Collections.emptyList());
+ }
+
+ public ClassLoadableResourceKeyPairProvider(String res) {
+ this(Collections.singletonList(ValidateUtils.checkNotNullAndNotEmpty(res, "No resource specified")));
+ }
+
+ public ClassLoadableResourceKeyPairProvider(ClassLoader cl, String res) {
+ this(cl, Collections.singletonList(ValidateUtils.checkNotNullAndNotEmpty(res, "No resource specified")));
+ }
+
+ public ClassLoadableResourceKeyPairProvider(Collection<String> resources) {
+ this.classLoader = ThreadUtils.resolveDefaultClassLoader(getClass());
+ this.resources = (resources == null) ? Collections.emptyList() : resources;
+ }
+
+ public ClassLoadableResourceKeyPairProvider(ClassLoader cl, Collection<String> resources) {
+ this.classLoader = cl;
+ this.resources = (resources == null) ? Collections.emptyList() : resources;
+ }
+
+ public Collection<String> getResources() {
+ return resources;
+ }
+
+ public void setResources(Collection<String> resources) {
+ this.resources = (resources == null) ? Collections.emptyList() : resources;
+ }
+
+ public ClassLoader getResourceLoader() {
+ return classLoader;
+ }
+
+ public void setResourceLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ @Override
+ public Iterable<KeyPair> loadKeys() {
+ return loadKeys(getResources());
+ }
+
+ @Override
+ protected InputStream openKeyPairResource(String resourceKey, String resource) throws IOException {
+ ClassLoader cl = resolveClassLoader();
+ if (cl == null) {
+ throw new StreamCorruptedException("No resource loader for " + resource);
+ }
+
+ InputStream input = cl.getResourceAsStream(resource);
+ if (input == null) {
+ throw new FileNotFoundException("Cannot find resource " + resource);
+ }
+
+ return input;
+ }
+
+ protected ClassLoader resolveClassLoader() {
+ ClassLoader cl = getResourceLoader();
+ if (cl == null) {
+ cl = ThreadUtils.resolveDefaultClassLoader(getClass());
+ }
+ return cl;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
new file mode 100644
index 0000000..c4aae97
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
@@ -0,0 +1,92 @@
+/*
+ * 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.keyprovider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+
+/**
+ * This host key provider loads private keys from the specified files. The
+ * loading is <U>lazy</U> - i.e., a file is not loaded until it is actually
+ * required. Once required though, its loaded {@link KeyPair} result is
+ * <U>cached</U> and not re-loaded.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class FileKeyPairProvider extends AbstractResourceKeyPairProvider<Path> {
+ private Collection<? extends Path> files;
+
+ public FileKeyPairProvider() {
+ super();
+ }
+
+ public FileKeyPairProvider(Path path) {
+ this(Collections.singletonList(Objects.requireNonNull(path, "No path provided")));
+ }
+
+ public FileKeyPairProvider(Path... files) {
+ this(Arrays.asList(files));
+ }
+
+ public FileKeyPairProvider(Collection<? extends Path> files) {
+ this.files = files;
+ }
+
+ public Collection<? extends Path> getPaths() {
+ return files;
+ }
+
+ public void setFiles(Collection<File> files) {
+ setPaths(GenericUtils.map(files, File::toPath));
+ }
+
+ public void setPaths(Collection<? extends Path> paths) {
+ // use absolute path in order to have unique cache keys
+ Collection<Path> resolved = GenericUtils.map(paths, Path::toAbsolutePath);
+ resetCacheMap(resolved);
+ files = resolved;
+ }
+
+ @Override
+ public Iterable<KeyPair> loadKeys() {
+ return loadKeys(getPaths());
+ }
+
+ @Override
+ protected KeyPair doLoadKey(Path resource) throws IOException, GeneralSecurityException {
+ return super.doLoadKey((resource == null) ? null : resource.toAbsolutePath());
+ }
+
+ @Override
+ protected InputStream openKeyPairResource(String resourceKey, Path resource) throws IOException {
+ return Files.newInputStream(resource, IoUtils.EMPTY_OPEN_OPTIONS);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
new file mode 100644
index 0000000..d0b35d9
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
@@ -0,0 +1,170 @@
+/*
+ * 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.keyprovider;
+
+import java.security.KeyPair;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface KeyIdentityProvider {
+ /**
+ * An "empty" implementation of {@link KeyIdentityProvider} that
+ * returns an empty group of key pairs
+ */
+ KeyIdentityProvider EMPTY_KEYS_PROVIDER = new KeyIdentityProvider() {
+ @Override
+ public Iterable<KeyPair> loadKeys() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String toString() {
+ return "EMPTY";
+ }
+ };
+
+ /**
+ * Invokes {@link KeyIdentityProvider#loadKeys()} and returns the result - ignores
+ * {@code null} providers (i.e., returns an empty iterable instance)
+ */
+ Function<KeyIdentityProvider, Iterable<KeyPair>> LOADER = p ->
+ (p == null) ? Collections.<KeyPair>emptyList() : p.loadKeys();
+
+ /**
+ * Load available keys.
+ *
+ * @return an {@link Iterable} instance of available keys - ignored if {@code null}
+ */
+ Iterable<KeyPair> loadKeys();
+
+ /**
+ * Creates a "unified" {@link Iterator} of {@link KeyPair}s out of 2 possible
+ * {@link KeyIdentityProvider}
+ *
+ * @param identities The registered keys identities
+ * @param keys Extra available key pairs
+ * @return The wrapping iterator
+ * @see #resolveKeyIdentityProvider(KeyIdentityProvider, KeyIdentityProvider)
+ */
+ static Iterator<KeyPair> iteratorOf(KeyIdentityProvider identities, KeyIdentityProvider keys) {
+ return iteratorOf(resolveKeyIdentityProvider(identities, keys));
+ }
+
+ /**
+ * Resolves a non-{@code null} iterator of the available keys
+ *
+ * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
+ * @return A non-{@code null} iterator - which may be empty if no provider or no keys
+ */
+ static Iterator<KeyPair> iteratorOf(KeyIdentityProvider provider) {
+ return GenericUtils.iteratorOf((provider == null) ? null : provider.loadKeys());
+ }
+
+ /**
+ * <P>Creates a "unified" {@link KeyIdentityProvider} out of 2 possible ones
+ * as follows:</P></BR>
+ * <UL>
+ * <LI>If both are {@code null} then return {@code null}.</LI>
+ * <LI>If either one is {@code null} then use the non-{@code null} one.</LI>
+ * <LI>If both are the same instance then use it.</U>
+ * <LI>Otherwise, returns a wrapper that groups both providers.</LI>
+ * </UL>
+ * @param identities The registered key pair identities
+ * @param keys The extra available key pairs
+ * @return The resolved provider
+ * @see #multiProvider(KeyIdentityProvider...)
+ */
+ static KeyIdentityProvider resolveKeyIdentityProvider(KeyIdentityProvider identities, KeyIdentityProvider keys) {
+ if ((keys == null) || (identities == keys)) {
+ return identities;
+ } else if (identities == null) {
+ return keys;
+ } else {
+ return multiProvider(identities, keys);
+ }
+ }
+
+ /**
+ * Wraps a group of {@link KeyIdentityProvider} into a single one
+ *
+ * @param providers The providers - ignored if {@code null}/empty (i.e., returns
+ * {@link #EMPTY_KEYS_PROVIDER})
+ * @return The wrapping provider
+ * @see #multiProvider(Collection)
+ */
+ static KeyIdentityProvider multiProvider(KeyIdentityProvider... providers) {
+ return multiProvider(GenericUtils.asList(providers));
+ }
+
+ /**
+ * Wraps a group of {@link KeyIdentityProvider} into a single one
+ *
+ * @param providers The providers - ignored if {@code null}/empty (i.e., returns
+ * {@link #EMPTY_KEYS_PROVIDER})
+ * @return The wrapping provider
+ */
+ static KeyIdentityProvider multiProvider(Collection<? extends KeyIdentityProvider> providers) {
+ return GenericUtils.isEmpty(providers) ? EMPTY_KEYS_PROVIDER : wrapKeyPairs(iterableOf(providers));
+ }
+
+ /**
+ * Wraps a group of {@link KeyIdentityProvider} into an {@link Iterable} of {@link KeyPair}s
+ *
+ * @param providers The group of providers - ignored if {@code null}/empty (i.e., returns an
+ * empty iterable instance)
+ * @return The wrapping iterable
+ */
+ static Iterable<KeyPair> iterableOf(Collection<? extends KeyIdentityProvider> providers) {
+ Iterable<Supplier<Iterable<KeyPair>>> keysSuppliers =
+ GenericUtils.<KeyIdentityProvider, Supplier<Iterable<KeyPair>>>wrapIterable(providers, p -> p::loadKeys);
+ return GenericUtils.multiIterableSuppliers(keysSuppliers);
+ }
+
+ /**
+ * Wraps a group of {@link KeyPair}s into a {@link KeyIdentityProvider}
+ *
+ * @param pairs The key pairs - ignored if {@code null}/empty (i.e., returns
+ * {@link #EMPTY_KEYS_PROVIDER}).
+ * @return The provider wrapper
+ */
+ static KeyIdentityProvider wrapKeyPairs(KeyPair... pairs) {
+ return wrapKeyPairs(GenericUtils.asList(pairs));
+ }
+
+ /**
+ * Wraps a group of {@link KeyPair}s into a {@link KeyIdentityProvider}
+ *
+ * @param pairs The key pairs {@link Iterable} - ignored if {@code null} (i.e., returns
+ * {@link #EMPTY_KEYS_PROVIDER}).
+ * @return The provider wrapper
+ */
+ static KeyIdentityProvider wrapKeyPairs(Iterable<KeyPair> pairs) {
+ return (pairs == null) ? EMPTY_KEYS_PROVIDER : () -> pairs;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
new file mode 100644
index 0000000..a816304
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
@@ -0,0 +1,181 @@
+/*
+ * 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.keyprovider;
+
+import java.security.KeyPair;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Provider for key pairs. This provider is used on the server side to provide
+ * the host key, or on the client side to provide the user key.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface KeyPairProvider extends KeyIdentityProvider {
+
+ /**
+ * SSH identifier for RSA keys
+ */
+ String SSH_RSA = "ssh-rsa";
+
+ /**
+ * SSH identifier for DSA keys
+ */
+ String SSH_DSS = "ssh-dss";
+
+ /**
+ * SSH identifier for ED25519 elliptic curve keys
+ */
+ String SSH_ED25519 = "ssh-ed25519";
+
+ /**
+ * SSH identifier for EC keys in NIST curve P-256
+ */
+ String ECDSA_SHA2_NISTP256 = ECCurves.nistp256.getKeyType();
+
+ /**
+ * SSH identifier for EC keys in NIST curve P-384
+ */
+ String ECDSA_SHA2_NISTP384 = ECCurves.nistp384.getKeyType();
+
+ /**
+ * SSH identifier for EC keys in NIST curve P-521
+ */
+ String ECDSA_SHA2_NISTP521 = ECCurves.nistp521.getKeyType();
+
+ /**
+ * A {@link KeyPairProvider} that has no keys
+ */
+ KeyPairProvider EMPTY_KEYPAIR_PROVIDER =
+ new KeyPairProvider() {
+ @Override
+ public KeyPair loadKey(String type) {
+ return null;
+ }
+
+ @Override
+ public Iterable<String> getKeyTypes() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Iterable<KeyPair> loadKeys() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String toString() {
+ return "EMPTY_KEYPAIR_PROVIDER";
+ }
+ };
+
+ /**
+ * Load a key of the specified type which can be "ssh-rsa", "ssh-dss",
+ * or "ecdsa-sha2-nistp{256,384,521}". If there is no key of this type, return
+ * {@code null}
+ *
+ * @param type the type of key to load
+ * @return a valid key pair or {@code null} if this type of key is not available
+ */
+ default KeyPair loadKey(String type) {
+ ValidateUtils.checkNotNullAndNotEmpty(type, "No key type to load");
+ return GenericUtils.stream(loadKeys())
+ .filter(key -> type.equals(KeyUtils.getKeyType(key)))
+ .findFirst()
+ .orElse(null);
+ }
+
+ /**
+ * @return The available {@link Iterable} key types in preferred order - never {@code null}
+ */
+ default Iterable<String> getKeyTypes() {
+ return GenericUtils.stream(loadKeys())
+ .map(KeyUtils::getKeyType)
+ .filter(GenericUtils::isNotEmpty)
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * Wrap the provided {@link KeyPair}s into a {@link KeyPairProvider}
+ *
+ * @param pairs The available pairs - ignored if {@code null}/empty (i.e.,
+ * returns {@link #EMPTY_KEYPAIR_PROVIDER})
+ * @return The provider wrapper
+ * @see #wrap(Iterable)
+ */
+ static KeyPairProvider wrap(KeyPair... pairs) {
+ return GenericUtils.isEmpty(pairs) ? EMPTY_KEYPAIR_PROVIDER : wrap(Arrays.asList(pairs));
+ }
+
+ /**
+ * Wrap the provided {@link KeyPair}s into a {@link KeyPairProvider}
+ *
+ * @param pairs The available pairs {@link Iterable} - ignored if {@code null} (i.e.,
+ * returns {@link #EMPTY_KEYPAIR_PROVIDER})
+ * @return The provider wrapper
+ */
+ static KeyPairProvider wrap(Iterable<KeyPair> pairs) {
+ return (pairs == null) ? EMPTY_KEYPAIR_PROVIDER : new KeyPairProvider() {
+ @Override
+ public Iterable<KeyPair> loadKeys() {
+ return pairs;
+ }
+
+ @Override
+ public KeyPair loadKey(String type) {
+ for (KeyPair kp : pairs) {
+ String t = KeyUtils.getKeyType(kp);
+ if (Objects.equals(type, t)) {
+ return kp;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public Iterable<String> getKeyTypes() {
+ // use a LinkedHashSet so as to preserve the order but avoid duplicates
+ Collection<String> types = new LinkedHashSet<>();
+ for (KeyPair kp : pairs) {
+ String t = KeyUtils.getKeyType(kp);
+ if (GenericUtils.isEmpty(t)) {
+ continue; // avoid unknown key types
+ }
+
+ if (!types.add(t)) {
+ continue; // debug breakpoint
+ }
+ }
+
+ return types;
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProviderHolder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProviderHolder.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProviderHolder.java
new file mode 100644
index 0000000..553d553
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProviderHolder.java
@@ -0,0 +1,35 @@
+/*
+ * 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.keyprovider;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface KeyPairProviderHolder {
+ /**
+ * Retrieve the <code>KeyPairProvider</code> that will be used to find
+ * the host key to use on the server side or the user key on the client side.
+ *
+ * @return the <code>KeyPairProvider</code>, never {@code null}
+ */
+ KeyPairProvider getKeyPairProvider();
+
+ void setKeyPairProvider(KeyPairProvider keyPairProvider);
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
new file mode 100644
index 0000000..bf3ec8b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MappedKeyPairProvider.java
@@ -0,0 +1,97 @@
+/*
+ * 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.keyprovider;
+
+import java.security.KeyPair;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.function.Function;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Holds a {@link Map} of {@link String}->{@link KeyPair} where the map key
+ * is the type and value is the associated {@link KeyPair}
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class MappedKeyPairProvider implements KeyPairProvider {
+ /**
+ * Transforms a {@link Map} of {@link String}->{@link KeyPair} to a
+ * {@link KeyPairProvider} where map key is the type and value is the
+ * associated {@link KeyPair}
+ */
+ public static final Function<Map<String, KeyPair>, KeyPairProvider> MAP_TO_KEY_PAIR_PROVIDER =
+ MappedKeyPairProvider::new;
+
+ private final Map<String, KeyPair> pairsMap;
+
+ public MappedKeyPairProvider(KeyPair... pairs) {
+ this(GenericUtils.isEmpty(pairs) ? Collections.emptyList() : Arrays.asList(pairs));
+ }
+
+ public MappedKeyPairProvider(Collection<? extends KeyPair> pairs) {
+ this(mapUniquePairs(pairs));
+ }
+
+ public MappedKeyPairProvider(Map<String, KeyPair> pairsMap) {
+ this.pairsMap = ValidateUtils.checkNotNullAndNotEmpty(pairsMap, "No pairs map provided");
+ }
+
+ @Override
+ public Iterable<KeyPair> loadKeys() {
+ return pairsMap.values();
+ }
+
+ @Override
+ public KeyPair loadKey(String type) {
+ return pairsMap.get(type);
+ }
+
+ @Override
+ public Iterable<String> getKeyTypes() {
+ return pairsMap.keySet();
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(getKeyTypes());
+ }
+
+ public static Map<String, KeyPair> mapUniquePairs(Collection<? extends KeyPair> pairs) {
+ if (GenericUtils.isEmpty(pairs)) {
+ return Collections.emptyMap();
+ }
+
+ Map<String, KeyPair> pairsMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ for (KeyPair kp : pairs) {
+ String keyType = ValidateUtils.checkNotNullAndNotEmpty(KeyUtils.getKeyType(kp), "Cannot determine key type");
+ KeyPair prev = pairsMap.put(keyType, kp);
+ ValidateUtils.checkTrue(prev == null, "Multiple keys of type=%s", keyType);
+ }
+
+ return pairsMap;
+ }
+}
[24/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java b/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
deleted file mode 100644
index ae39dd3..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/ECCurves.java
+++ /dev/null
@@ -1,580 +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.cipher;
-
-import java.io.ByteArrayOutputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.security.interfaces.ECKey;
-import java.security.spec.ECField;
-import java.security.spec.ECFieldFp;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.EllipticCurve;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.NavigableSet;
-import java.util.Objects;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.OptionalFeature;
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.digest.BuiltinDigests;
-import org.apache.sshd.common.digest.Digest;
-import org.apache.sshd.common.digest.DigestFactory;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Utilities for working with elliptic curves.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum ECCurves implements NamedResource, OptionalFeature {
- nistp256(Constants.NISTP256, new int[]{1, 2, 840, 10045, 3, 1, 7},
- new ECParameterSpec(
- new EllipticCurve(
- new ECFieldFp(new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16)),
- new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16),
- new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)),
- new ECPoint(
- new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16),
- new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16)),
- new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16),
- 1),
- 32,
- BuiltinDigests.sha256),
- nistp384(Constants.NISTP384, new int[]{1, 3, 132, 0, 34},
- new ECParameterSpec(
- new EllipticCurve(
- new ECFieldFp(new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", 16)),
- new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", 16),
- new BigInteger("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", 16)),
- new ECPoint(
- new BigInteger("AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", 16),
- new BigInteger("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", 16)),
- new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", 16),
- 1),
- 48,
- BuiltinDigests.sha384),
- nistp521(Constants.NISTP521, new int[]{1, 3, 132, 0, 35},
- new ECParameterSpec(
- new EllipticCurve(
- new ECFieldFp(new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
- + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)),
- new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
- + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", 16),
- new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951"
- + "EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", 16)),
- new ECPoint(
- new BigInteger("00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77"
- + "EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", 16),
- new BigInteger("011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE7299"
- + "5EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", 16)),
- new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B"
- + "7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16),
- 1),
- 66,
- BuiltinDigests.sha512);
-
- /**
- * A {@link Set} of all the known curves
- */
- public static final Set<ECCurves> VALUES =
- Collections.unmodifiableSet(EnumSet.allOf(ECCurves.class));
-
- /**
- * A {@link Set} of all the known curves names
- */
- public static final NavigableSet<String> NAMES =
- Collections.unmodifiableNavigableSet(GenericUtils.mapSort(
- VALUES,
- ECCurves::getName,
- String.CASE_INSENSITIVE_ORDER));
-
- /**
- * A {@link Set} of all the known curves key types
- */
- public static final NavigableSet<String> KEY_TYPES =
- Collections.unmodifiableNavigableSet(GenericUtils.mapSort(
- VALUES,
- ECCurves::getKeyType,
- String.CASE_INSENSITIVE_ORDER));
-
- public static final Comparator<ECCurves> BY_KEY_SIZE = (o1, o2) -> {
- int k1 = (o1 == null) ? Integer.MAX_VALUE : o1.getKeySize();
- int k2 = (o2 == null) ? Integer.MAX_VALUE : o2.getKeySize();
- return Integer.compare(k1, k2);
- };
-
- public static final List<ECCurves> SORTED_KEY_SIZE =
- Collections.unmodifiableList(VALUES.stream()
- .sorted(BY_KEY_SIZE)
- .collect(Collectors.toList()));
-
- private final String name;
- private final String keyType;
- private final String oidString;
- private final List<Integer> oidValue;
- private final ECParameterSpec params;
- private final int keySize;
- private final int numOctets;
- private final DigestFactory digestFactory;
-
- ECCurves(String name, int[] oid, ECParameterSpec params, int numOctets, DigestFactory digestFactory) {
- this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No curve name");
- this.oidString = NumberUtils.join('.', ValidateUtils.checkNotNullAndNotEmpty(oid, "No OID"));
- this.oidValue = Collections.unmodifiableList(NumberUtils.asList(oid));
- this.keyType = Constants.ECDSA_SHA2_PREFIX + name;
- this.params = ValidateUtils.checkNotNull(params, "No EC params for %s", name);
- this.keySize = getCurveSize(params);
- this.numOctets = numOctets;
- this.digestFactory = Objects.requireNonNull(digestFactory, "No digestFactory");
- }
-
- @Override // The curve name
- public final String getName() {
- return name;
- }
-
- public final String getOID() {
- return oidString;
- }
-
- public final List<Integer> getOIDValue() {
- return oidValue;
- }
-
- /**
- * @return The standard key type used to represent this curve
- */
- public final String getKeyType() {
- return keyType;
- }
-
- @Override
- public final boolean isSupported() {
- return SecurityUtils.isECCSupported() && digestFactory.isSupported();
- }
-
- public final ECParameterSpec getParameters() {
- return params;
- }
-
- /**
- * @return The size (in bits) of the key
- */
- public final int getKeySize() {
- return keySize;
- }
-
- /**
- * @return The number of octets used to represent the point(s) for the curve
- */
- public final int getNumPointOctets() {
- return numOctets;
- }
-
- /**
- * @return The {@link Digest} to use when hashing the curve's parameters
- */
- public final Digest getDigestForParams() {
- return digestFactory.create();
- }
-
- /**
- * @param type The key type value - ignored if {@code null}/empty
- * @return The matching {@link ECCurves} constant - {@code null} if
- * no match found case <U>insensitive</U>
- */
- public static ECCurves fromKeyType(String type) {
- if (GenericUtils.isEmpty(type)) {
- return null;
- }
-
- for (ECCurves c : VALUES) {
- if (type.equalsIgnoreCase(c.getKeyType())) {
- return c;
- }
- }
-
- return null;
- }
-
- /**
- * @param name The curve name (case <U>insensitive</U> - ignored if
- * {@code null}/empty
- * @return The matching {@link ECCurves} instance - {@code null} if no
- * match found
- */
- public static ECCurves fromCurveName(String name) {
- return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
- }
-
- /**
- * @param key The {@link ECKey} - ignored if {@code null}
- * @return The matching {@link ECCurves} instance - {@code null} if no
- * match found
- */
- public static ECCurves fromECKey(ECKey key) {
- return fromCurveParameters((key == null) ? null : key.getParams());
- }
-
- /**
- * @param params The curve's {@link ECParameterSpec} - ignored if {@code null}
- * @return The matching {@link ECCurves} value - {@code null} if no match found
- * @see #getCurveSize(ECParameterSpec)
- * @see #fromCurveSize(int)
- */
- public static ECCurves fromCurveParameters(ECParameterSpec params) {
- if (params == null) {
- return null;
- } else {
- return fromCurveSize(getCurveSize(params));
- }
- }
-
- /**
- * @param keySize The key size (in bits)
- * @return The matching {@link ECCurves} value - {@code null} if no
- * match found
- */
- public static ECCurves fromCurveSize(int keySize) {
- if (keySize <= 0) {
- return null;
- }
-
- for (ECCurves c : VALUES) {
- if (keySize == c.getKeySize()) {
- return c;
- }
- }
-
- return null;
- }
-
- public static ECCurves fromOIDValue(List<? extends Number> oid) {
- if (GenericUtils.isEmpty(oid)) {
- return null;
- }
-
- for (ECCurves c : VALUES) {
- List<? extends Number> v = c.getOIDValue();
- if (oid.size() != v.size()) {
- continue;
- }
-
- boolean matches = true;
- for (int index = 0; index < v.size(); index++) {
- Number exp = v.get(index);
- Number act = oid.get(index);
- if (exp.intValue() != act.intValue()) {
- matches = false;
- break;
- }
- }
-
- if (matches) {
- return c;
- }
- }
-
- return null;
- }
-
- public static ECCurves fromOID(String oid) {
- if (GenericUtils.isEmpty(oid)) {
- return null;
- }
-
- for (ECCurves c : VALUES) {
- if (oid.equalsIgnoreCase(c.getOID())) {
- return c;
- }
- }
-
- return null;
- }
-
- /**
- * @param params The curve's {@link ECParameterSpec}
- * @return The curve's key size in bits
- * @throws IllegalArgumentException if invalid parameters provided
- */
- public static int getCurveSize(ECParameterSpec params) {
- EllipticCurve curve = Objects.requireNonNull(params, "No EC params").getCurve();
- ECField field = Objects.requireNonNull(curve, "No EC curve").getField();
- return Objects.requireNonNull(field, "No EC field").getFieldSize();
- }
-
- public static byte[] encodeECPoint(ECPoint group, ECParameterSpec params) {
- return encodeECPoint(group, params.getCurve());
- }
-
- public static byte[] encodeECPoint(ECPoint group, EllipticCurve curve) {
- // M has len 2 ceil(log_2(q)/8) + 1 ?
- int elementSize = (curve.getField().getFieldSize() + 7) / 8;
- byte[] m = new byte[2 * elementSize + 1];
-
- // Uncompressed format
- m[0] = 0x04;
-
- byte[] affineX = removeLeadingZeroes(group.getAffineX().toByteArray());
- System.arraycopy(affineX, 0, m, 1 + elementSize - affineX.length, affineX.length);
-
- byte[] affineY = removeLeadingZeroes(group.getAffineY().toByteArray());
- System.arraycopy(affineY, 0, m, 1 + elementSize + elementSize - affineY.length, affineY.length);
-
- return m;
- }
-
- private static byte[] removeLeadingZeroes(byte[] input) {
- if (input[0] != 0x00) {
- return input;
- }
-
- int pos = 1;
- while (pos < input.length - 1 && input[pos] == 0x00) {
- pos++;
- }
-
- byte[] output = new byte[input.length - pos];
- System.arraycopy(input, pos, output, 0, output.length);
- return output;
- }
-
- /**
- * Converts the given octet string (defined by ASN.1 specifications) to a {@link BigInteger}
- * As octet strings always represent positive integers, a zero-byte is prepended to
- * the given array if necessary (if is MSB equal to 1), then this is converted to BigInteger
- * The conversion is defined in the Section 2.3.8
- *
- * @param octets - octet string bytes to be converted
- * @return The {@link BigInteger} representation of the octet string
- */
- public static BigInteger octetStringToInteger(byte... octets) {
- if (octets == null) {
- return null;
- } else if (octets.length == 0) {
- return BigInteger.ZERO;
- } else {
- return new BigInteger(1, octets);
- }
- }
-
- public static ECPoint octetStringToEcPoint(byte... octets) {
- if (NumberUtils.isEmpty(octets)) {
- return null;
- }
-
- int startIndex = findFirstNonZeroIndex(octets);
- if (startIndex < 0) {
- throw new IllegalArgumentException("All zeroes ECPoint N/A");
- }
-
- byte indicator = octets[startIndex];
- ECCurves.ECPointCompression compression = ECCurves.ECPointCompression.fromIndicatorValue(indicator);
- if (compression == null) {
- throw new UnsupportedOperationException("Unknown compression indicator value: 0x" + Integer.toHexString(indicator & 0xFF));
- }
-
- // The coordinates actually start after the compression indicator
- return compression.octetStringToEcPoint(octets, startIndex + 1, octets.length - startIndex - 1);
- }
-
- private static int findFirstNonZeroIndex(byte... octets) {
- if (NumberUtils.isEmpty(octets)) {
- return -1;
- }
-
- for (int index = 0; index < octets.length; index++) {
- if (octets[index] != 0) {
- return index;
- }
- }
-
- return -1; // all zeroes
- }
-
- public static final class Constants {
- /**
- * Standard prefix of NISTP key types when encoded
- */
- public static final String ECDSA_SHA2_PREFIX = "ecdsa-sha2-";
-
- public static final String NISTP256 = "nistp256";
- public static final String NISTP384 = "nistp384";
- public static final String NISTP521 = "nistp521";
-
- private Constants() {
- throw new UnsupportedOperationException("No instance allowed");
- }
- }
-
- /**
- * The various {@link ECPoint} representation compression indicators
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="https://www.ietf.org/rfc/rfc5480.txt">RFC-5480 - section 2.2</A>
- */
- public enum ECPointCompression {
- // see http://tools.ietf.org/html/draft-jivsov-ecc-compact-00
- // see http://crypto.stackexchange.com/questions/8914/ecdsa-compressed-public-key-point-back-to-uncompressed-public-key-point
- VARIANT2((byte) 0x02) {
- @Override
- public ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len) {
- byte[] xp = new byte[len];
- System.arraycopy(octets, startIndex, xp, 0, len);
- BigInteger x = octetStringToInteger(xp);
-
- // TODO derive even Y...
- throw new UnsupportedOperationException("octetStringToEcPoint(" + name() + ")(X=" + x + ") compression support N/A");
- }
- },
- VARIANT3((byte) 0x03) {
- @Override
- public ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len) {
- byte[] xp = new byte[len];
- System.arraycopy(octets, startIndex, xp, 0, len);
- BigInteger x = octetStringToInteger(xp);
-
- // TODO derive odd Y...
- throw new UnsupportedOperationException("octetStringToEcPoint(" + name() + ")(X=" + x + ") compression support N/A");
- }
- },
- UNCOMPRESSED((byte) 0x04) {
- @Override
- public ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len) {
- int numElements = len / 2; /* x, y */
- if (len != (numElements * 2)) { // make sure length is not odd
- throw new IllegalArgumentException("octetStringToEcPoint(" + name() + ") "
- + " invalid remainder octets representation: "
- + " expected=" + (2 * numElements) + ", actual=" + len);
- }
-
- byte[] xp = new byte[numElements];
- byte[] yp = new byte[numElements];
- System.arraycopy(octets, startIndex, xp, 0, numElements);
- System.arraycopy(octets, startIndex + numElements, yp, 0, numElements);
-
- BigInteger x = octetStringToInteger(xp);
- BigInteger y = octetStringToInteger(yp);
- return new ECPoint(x, y);
- }
-
- @Override
- public void writeECPoint(OutputStream s, String curveName, ECPoint p) throws IOException {
- ECCurves curve = fromCurveName(curveName);
- if (curve == null) {
- throw new StreamCorruptedException("writeECPoint(" + name() + ")[" + curveName + "] cannot determine octets count");
- }
-
- int numElements = curve.getNumPointOctets();
- KeyEntryResolver.encodeInt(s, 1 /* the indicator */ + 2 * numElements);
- s.write(getIndicatorValue());
- writeCoordinate(s, "X", p.getAffineX(), numElements);
- writeCoordinate(s, "Y", p.getAffineY(), numElements);
- }
- };
-
- public static final Set<ECPointCompression> VALUES =
- Collections.unmodifiableSet(EnumSet.allOf(ECPointCompression.class));
-
- private final byte indicatorValue;
-
- ECPointCompression(byte indicator) {
- indicatorValue = indicator;
- }
-
- public final byte getIndicatorValue() {
- return indicatorValue;
- }
-
- public abstract ECPoint octetStringToEcPoint(byte[] octets, int startIndex, int len);
-
- public byte[] ecPointToOctetString(String curveName, ECPoint p) {
- try (ByteArrayOutputStream baos = new ByteArrayOutputStream((2 * 66) + Long.SIZE)) {
- writeECPoint(baos, curveName, p);
- return baos.toByteArray();
- } catch (IOException e) {
- throw new RuntimeException("ecPointToOctetString(" + curveName + ")"
- + " failed (" + e.getClass().getSimpleName() + ")"
- + " to write data: " + e.getMessage(),
- e);
- }
- }
-
- public void writeECPoint(OutputStream s, String curveName, ECPoint p) throws IOException {
- if (s == null) {
- throw new EOFException("No output stream");
- }
-
- throw new StreamCorruptedException("writeECPoint(" + name() + ")[" + p + "] N/A");
- }
-
- protected void writeCoordinate(OutputStream s, String n, BigInteger v, int numElements) throws IOException {
- byte[] vp = v.toByteArray();
- int startIndex = 0;
- int vLen = vp.length;
- if (vLen > numElements) {
- if (vp[0] == 0) { // skip artificial positive sign
- startIndex++;
- vLen--;
- }
- }
-
- if (vLen > numElements) {
- throw new StreamCorruptedException("writeCoordinate(" + name() + ")[" + n + "]"
- + " value length (" + vLen + ") exceeds max. (" + numElements + ")"
- + " for " + v);
- }
-
- if (vLen < numElements) {
- byte[] tmp = new byte[numElements];
- System.arraycopy(vp, startIndex, tmp, numElements - vLen, vLen);
- vp = tmp;
- }
-
- s.write(vp, startIndex, vLen);
- }
-
- public static ECPointCompression fromIndicatorValue(int value) {
- if ((value < 0) || (value > 0xFF)) {
- return null; // must be a byte value
- }
-
- for (ECPointCompression c : VALUES) {
- if (value == c.getIndicatorValue()) {
- return c;
- }
- }
-
- return null;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/cipher/package.html
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/cipher/package.html b/sshd-core/src/main/java/org/apache/sshd/common/cipher/package.html
deleted file mode 100644
index 197a89d..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/cipher/package.html
+++ /dev/null
@@ -1,26 +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.
--->
-<html>
-<head>
-</head>
-<body>
-
-<a href="{@docRoot}/org/apache/sshd/common/cipher/Cipher.html"><code>Cipher</code></a>
-implementations.
-
-</body>
-</html>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/BaseCompression.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
deleted file mode 100644
index ff31947..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/BaseCompression.java
+++ /dev/null
@@ -1,48 +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.compression;
-
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class BaseCompression implements Compression {
- private final String name;
-
- protected BaseCompression(String name) {
- this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No compression name");
- }
-
- @Override
- public final String getName() {
- return name;
- }
-
- @Override
- public boolean isCompressionExecuted() {
- return true;
- }
-
- @Override
- public String toString() {
- return getName();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
deleted file mode 100644
index 49ad0ab..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/BuiltinCompressions.java
+++ /dev/null
@@ -1,239 +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.compression;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableSet;
-import java.util.Objects;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeMap;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.config.NamedFactoriesListParseResult;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum BuiltinCompressions implements CompressionFactory {
- none(Constants.NONE) {
- @Override
- public Compression create() {
- return new CompressionNone();
- }
-
- @Override
- public boolean isCompressionExecuted() {
- return false;
- }
- },
- zlib(Constants.ZLIB) {
- @Override
- public Compression create() {
- return new CompressionZlib();
- }
- },
- delayedZlib(Constants.DELAYED_ZLIB) {
- @Override
- public Compression create() {
- return new CompressionDelayedZlib();
- }
-
- @Override
- public boolean isDelayed() {
- return true;
- }
- };
-
- public static final Set<BuiltinCompressions> VALUES =
- Collections.unmodifiableSet(EnumSet.allOf(BuiltinCompressions.class));
-
- private static final Map<String, CompressionFactory> EXTENSIONS =
- new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
- private final String name;
-
- BuiltinCompressions(String n) {
- name = n;
- }
-
- @Override
- public final String getName() {
- return name;
- }
-
- @Override
- public boolean isDelayed() {
- return false;
- }
-
- @Override
- public boolean isCompressionExecuted() {
- return true;
- }
-
- @Override
- public final String toString() {
- return getName();
- }
-
- @Override
- public final boolean isSupported() {
- return true;
- }
-
- /**
- * Registered a {@link org.apache.sshd.common.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(CompressionFactory 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<CompressionFactory> 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 CompressionFactory unregisterExtension(String name) {
- if (GenericUtils.isEmpty(name)) {
- return null;
- }
-
- synchronized (EXTENSIONS) {
- return EXTENSIONS.remove(name);
- }
- }
-
- public static BuiltinCompressions fromFactoryName(String name) {
- return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
- }
-
- /**
- * @param compressions A comma-separated list of Compressions' names - ignored
- * if {@code null}/empty
- * @return A {@link ParseResult} containing the successfully parsed
- * factories and the unknown ones. <B>Note:</B> it is up to caller to
- * ensure that the lists do not contain duplicates
- */
- public static ParseResult parseCompressionsList(String compressions) {
- return parseCompressionsList(GenericUtils.split(compressions, ','));
- }
-
- public static ParseResult parseCompressionsList(String... compressions) {
- return parseCompressionsList(GenericUtils.isEmpty((Object[]) compressions) ? Collections.emptyList() : Arrays.asList(compressions));
- }
-
- public static ParseResult parseCompressionsList(Collection<String> compressions) {
- if (GenericUtils.isEmpty(compressions)) {
- return ParseResult.EMPTY;
- }
-
- List<CompressionFactory> factories = new ArrayList<>(compressions.size());
- List<String> unknown = Collections.emptyList();
- for (String name : compressions) {
- CompressionFactory c = resolveFactory(name);
- if (c != null) {
- factories.add(c);
- } else {
- // replace the (unmodifiable) empty list with a real one
- if (unknown.isEmpty()) {
- unknown = new ArrayList<>();
- }
- 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 CompressionFactory resolveFactory(String name) {
- if (GenericUtils.isEmpty(name)) {
- return null;
- }
-
- CompressionFactory c = fromFactoryName(name);
- if (c != null) {
- return c;
- }
-
- synchronized (EXTENSIONS) {
- return EXTENSIONS.get(name);
- }
- }
-
- /**
- * Holds the result of {@link BuiltinCompressions#parseCompressionsList(String)}
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
- public static class ParseResult extends NamedFactoriesListParseResult<Compression, CompressionFactory> {
- public static final ParseResult EMPTY = new ParseResult(Collections.emptyList(), Collections.emptyList());
-
- public ParseResult(List<CompressionFactory> parsed, List<String> unsupported) {
- super(parsed, unsupported);
- }
- }
-
- public static final class Constants {
- public static final String NONE = "none";
- public static final String ZLIB = "zlib";
- public static final String DELAYED_ZLIB = "zlib@openssh.com";
-
- 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/compression/Compression.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/Compression.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/Compression.java
deleted file mode 100644
index 91ac27e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/Compression.java
+++ /dev/null
@@ -1,71 +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.compression;
-
-import java.io.IOException;
-
-import org.apache.sshd.common.util.buffer.Buffer;
-
-/**
- * Interface used to compress the stream of data between the
- * SSH server and clients.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Compression extends CompressionInformation {
-
- /**
- * Enum identifying if this object will be used to compress
- * or uncompress data.
- */
- enum Type {
- Inflater,
- Deflater
- }
-
- /**
- * Initialize this object to either compress or uncompress data.
- * This method must be called prior to any calls to either
- * <code>compress</code> or <code>uncompress</code>.
- * Once the object has been initialized, only one of
- * <code>compress</code> or <code>uncompress</code> methods can be
- * called.
- *
- * @param type compression type
- * @param level compression level
- */
- void init(Type type, int level);
-
- /**
- * Compress the given buffer in place.
- *
- * @param buffer the buffer containing the data to compress
- * @throws IOException if an error occurs
- */
- void compress(Buffer buffer) throws IOException;
-
- /**
- * Uncompress the data in a buffer into another buffer.
- *
- * @param from the buffer containing the data to uncompress
- * @param to the buffer receiving the uncompressed data
- * @throws IOException if an error occurs
- */
- void uncompress(Buffer from, Buffer to) throws IOException;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
deleted file mode 100644
index c062e38..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionDelayedZlib.java
+++ /dev/null
@@ -1,40 +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.compression;
-
-
-/**
- * ZLib delayed compression.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see Compression#isDelayed()
- */
-public class CompressionDelayedZlib extends CompressionZlib {
- /**
- * Create a new instance of a delayed ZLib compression
- */
- public CompressionDelayedZlib() {
- super(BuiltinCompressions.Constants.DELAYED_ZLIB);
- }
-
- @Override
- public boolean isDelayed() {
- return true;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
deleted file mode 100644
index 355594a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionFactory.java
+++ /dev/null
@@ -1,31 +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.compression;
-
-import org.apache.sshd.common.BuiltinFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-// CHECKSTYLE:OFF
-public interface CompressionFactory extends BuiltinFactory<Compression>, CompressionInformation {
- // nothing extra
-}
-//CHECKSTYLE:ON
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionInformation.java
deleted file mode 100644
index 428689f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionInformation.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.compression;
-
-import org.apache.sshd.common.NamedResource;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface CompressionInformation extends NamedResource {
- /**
- * Delayed compression is an Open-SSH specific feature which
- * informs both the client and server to not compress data before
- * the session has been authenticated.
- *
- * @return if the compression is delayed after authentication or not
- */
- boolean isDelayed();
-
- /**
- * @return {@code true} if there is any compression executed by
- * this "compressor" - special case for 'none'
- */
- boolean isCompressionExecuted();
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionNone.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
deleted file mode 100644
index 815e8ce..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionNone.java
+++ /dev/null
@@ -1,76 +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.compression;
-
-import java.io.IOException;
-import java.io.StreamCorruptedException;
-
-import org.apache.sshd.common.util.buffer.Buffer;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class CompressionNone extends BaseCompression {
- private Type type;
- private int level;
-
- public CompressionNone() {
- super(BuiltinCompressions.Constants.NONE);
- }
-
- @Override
- public void init(Type type, int level) {
- this.type = type;
- this.level = level;
- }
-
- @Override
- public boolean isCompressionExecuted() {
- return false;
- }
-
- @Override
- public void compress(Buffer buffer) throws IOException {
- if (!Type.Deflater.equals(type)) {
- throw new StreamCorruptedException("Not set up for compression: " + type);
- }
- }
-
- @Override
- public void uncompress(Buffer from, Buffer to) throws IOException {
- if (!Type.Inflater.equals(type)) {
- throw new StreamCorruptedException("Not set up for de-compression: " + type);
- }
-
- if (from != to) {
- throw new StreamCorruptedException("Separate de-compression buffers provided");
- }
- }
-
- @Override
- public boolean isDelayed() {
- return false;
- }
-
- @Override
- public String toString() {
- return super.toString() + "[" + type + "/" + level + "]";
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java b/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
deleted file mode 100644
index e365b91..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/CompressionZlib.java
+++ /dev/null
@@ -1,85 +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.compression;
-
-import java.io.IOException;
-import java.util.zip.DataFormatException;
-import java.util.zip.Deflater;
-import java.util.zip.Inflater;
-
-import org.apache.sshd.common.util.buffer.Buffer;
-
-/**
- * ZLib based Compression.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class CompressionZlib extends BaseCompression {
-
- private static final int BUF_SIZE = 4096;
-
- private byte[] tmpbuf = new byte[BUF_SIZE];
- private Deflater compresser;
- private Inflater decompresser;
-
- /**
- * Create a new instance of a ZLib base compression
- */
- public CompressionZlib() {
- this(BuiltinCompressions.Constants.ZLIB);
- }
-
- protected CompressionZlib(String name) {
- super(name);
- }
-
- @Override
- public boolean isDelayed() {
- return false;
- }
-
- @Override
- public void init(Type type, int level) {
- compresser = new Deflater(level);
- decompresser = new Inflater();
- }
-
- @Override
- public void compress(Buffer buffer) throws IOException {
- compresser.setInput(buffer.array(), buffer.rpos(), buffer.available());
- buffer.wpos(buffer.rpos());
- for (int len = compresser.deflate(tmpbuf, 0, tmpbuf.length, Deflater.SYNC_FLUSH);
- len > 0;
- len = compresser.deflate(tmpbuf, 0, tmpbuf.length, Deflater.SYNC_FLUSH)) {
- buffer.putRawBytes(tmpbuf, 0, len);
- }
- }
-
- @Override
- public void uncompress(Buffer from, Buffer to) throws IOException {
- decompresser.setInput(from.array(), from.rpos(), from.available());
- try {
- for (int len = decompresser.inflate(tmpbuf); len > 0; len = decompresser.inflate(tmpbuf)) {
- to.putRawBytes(tmpbuf, 0, len);
- }
- } catch (DataFormatException e) {
- throw new IOException("Error decompressing data", e);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/compression/package.html
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/compression/package.html b/sshd-core/src/main/java/org/apache/sshd/common/compression/package.html
deleted file mode 100644
index 9bd4652..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/compression/package.html
+++ /dev/null
@@ -1,25 +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.
--->
-<html>
-<head>
-</head>
-<body>
-
-<a href="{@docRoot}/org/apache/sshd/common/compression/Compression.html"><code>Compression</code></a> implementations.
-
-</body>
-</html>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java b/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
deleted file mode 100644
index e153adc..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/CompressionConfigValue.java
+++ /dev/null
@@ -1,94 +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.compression.BuiltinCompressions;
-import org.apache.sshd.common.compression.Compression;
-import org.apache.sshd.common.compression.CompressionFactory;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * Provides a "bridge" between the configuration values and the
- * actual {@link org.apache.sshd.common.NamedFactory} for the {@link Compression}.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum CompressionConfigValue implements CompressionFactory {
- YES(BuiltinCompressions.zlib),
- NO(BuiltinCompressions.none),
- DELAYED(BuiltinCompressions.delayedZlib);
-
- public static final Set<CompressionConfigValue> VALUES =
- Collections.unmodifiableSet(EnumSet.allOf(CompressionConfigValue.class));
-
- private final CompressionFactory factory;
-
- CompressionConfigValue(CompressionFactory delegate) {
- factory = delegate;
- }
-
- @Override
- public final String getName() {
- return factory.getName();
- }
-
- @Override
- public final Compression create() {
- return factory.create();
- }
-
- @Override
- public boolean isSupported() {
- return factory.isSupported();
- }
-
- @Override
- public final String toString() {
- return getName();
- }
-
- @Override
- public boolean isDelayed() {
- return factory.isDelayed();
- }
-
- @Override
- public boolean isCompressionExecuted() {
- return factory.isCompressionExecuted();
- }
-
- public static CompressionConfigValue fromName(String n) {
- if (GenericUtils.isEmpty(n)) {
- return null;
- }
-
- for (CompressionConfigValue v : VALUES) {
- if (n.equalsIgnoreCase(v.name())) {
- return v;
- }
- }
-
- return null;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java b/sshd-core/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.java
deleted file mode 100644
index 7a1cee1..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/FactoriesListParseResult.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.List;
-
-import org.apache.sshd.common.Factory;
-
-/**
- * @param <T> Result type
- * @param <F> Factory type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class FactoriesListParseResult<T, F extends Factory<T>> extends ListParseResult<F> {
- protected FactoriesListParseResult(List<F> parsed, List<String> unsupported) {
- super(parsed, unsupported);
- }
-
- /**
- * @return The {@link List} of successfully parsed {@link Factory} instances
- * in the <U>same order</U> as they were encountered during parsing
- */
- public final List<F> getParsedFactories() {
- return getParsedValues();
- }
-
- /**
- * @return A {@link List} of unknown/unsupported configuration values for
- * the factories
- */
- public List<String> getUnsupportedFactories() {
- return getUnsupportedValues();
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/ListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/ListParseResult.java b/sshd-core/src/main/java/org/apache/sshd/common/config/ListParseResult.java
deleted file mode 100644
index 97590cb..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/ListParseResult.java
+++ /dev/null
@@ -1,66 +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.List;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * Used to hold the result of parsing a list of value. Such result contains known
- * and unknown values - which are accessible via the respective {@link #getParsedValues()}
- * and {@link #getUnsupportedValues()} methods. <B>Note:</B> the returned {@link List}s may
- * be un-modifiable, so it is recommended to avoid attempting changing the, returned
- * list(s)
- *
- * @param <E> Type of list item
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class ListParseResult<E> {
- private final List<E> parsed;
- private final List<String> unsupported;
-
- protected ListParseResult(List<E> parsed, List<String> unsupported) {
- this.parsed = parsed;
- this.unsupported = unsupported;
- }
-
- /**
- * @return The {@link List} of successfully parsed value instances
- * in the <U>same order</U> as they were encountered during parsing
- */
- public final List<E> getParsedValues() {
- return parsed;
- }
-
- /**
- * @return A {@link List} of unknown/unsupported configuration values for
- * the factories
- */
- public List<String> getUnsupportedValues() {
- return unsupported;
- }
-
- @Override
- public String toString() {
- return "parsed=" + GenericUtils.join(getParsedValues(), ',')
- + ";unsupported=" + GenericUtils.join(getUnsupportedValues(), ',');
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/LogLevelValue.java b/sshd-core/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
deleted file mode 100644
index 2fbdc80..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/LogLevelValue.java
+++ /dev/null
@@ -1,56 +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:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html"><I>LogLevel</I> configuration value</A>
- */
-public enum LogLevelValue {
- /*
- * NOTE(s):
- * 1. DEBUG and DEBUG1 are EQUIVALENT
- * 2. Order is important (!!!)
- */
- QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, DEBUG3;
-
- public static final Set<LogLevelValue> VALUES =
- Collections.unmodifiableSet(EnumSet.allOf(LogLevelValue.class));
-
- public static LogLevelValue fromName(String n) {
- if (GenericUtils.isEmpty(n)) {
- return null;
- }
-
- for (LogLevelValue l : VALUES) {
- if (n.equalsIgnoreCase(l.name())) {
- return l;
- }
- }
-
- return null;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java b/sshd-core/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java
deleted file mode 100644
index 246cae0..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/NamedFactoriesListParseResult.java
+++ /dev/null
@@ -1,47 +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.List;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * Holds the result of parsing a list of {@link NamedFactory}ies
- *
- * @param <T> Result type
- * @param <F> Factory type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class NamedFactoriesListParseResult<T, F extends NamedFactory<T>>
- extends FactoriesListParseResult<T, F> {
-
- protected NamedFactoriesListParseResult(List<F> parsed, List<String> unsupported) {
- super(parsed, unsupported);
- }
-
- @Override
- public String toString() {
- return "parsed=" + NamedResource.getNames(getParsedFactories())
- + ";unknown=" + GenericUtils.join(getUnsupportedFactories(), ',');
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java b/sshd-core/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java
deleted file mode 100644
index feb45f4..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/NamedResourceListParseResult.java
+++ /dev/null
@@ -1,57 +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.List;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @param <R> Type of result {@link NamedResource}
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class NamedResourceListParseResult<R extends NamedResource> extends ListParseResult<R> {
- protected NamedResourceListParseResult(List<R> parsed, List<String> unsupported) {
- super(parsed, unsupported);
- }
-
- /**
- * @return The {@link List} of successfully parsed {@link NamedResource} instances
- * in the <U>same order</U> as they were encountered during parsing
- */
- public final List<R> getParsedResources() {
- return getParsedValues();
- }
-
- /**
- * @return A {@link List} of unknown/unsupported configuration values for
- * the resources
- */
- public List<String> getUnsupportedResources() {
- return getUnsupportedValues();
- }
-
- @Override
- public String toString() {
- return "parsed=" + NamedResource.getNames(getParsedResources())
- + ";unknown=" + GenericUtils.join(getUnsupportedResources(), ',');
- }
-}
[45/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
new file mode 100644
index 0000000..23937c2
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
@@ -0,0 +1,937 @@
+/*
+ * 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.math.BigInteger;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermission;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.DSAKey;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAKey;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.impl.DSSPublicKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.impl.ECDSAPublicKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.impl.RSAPublicKeyDecoder;
+import org.apache.sshd.common.digest.BuiltinDigests;
+import org.apache.sshd.common.digest.Digest;
+import org.apache.sshd.common.digest.DigestFactory;
+import org.apache.sshd.common.digest.DigestUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.OsUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Utility class for keys
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class KeyUtils {
+ /**
+ * Name of algorithm for RSA keys to be used when calling security provider
+ */
+ public static final String RSA_ALGORITHM = "RSA";
+
+ /**
+ * The most commonly used RSA public key exponent
+ */
+ public static final BigInteger DEFAULT_RSA_PUBLIC_EXPONENT = new BigInteger("65537");
+
+ /**
+ * Name of algorithm for DSS keys to be used when calling security provider
+ */
+ public static final String DSS_ALGORITHM = "DSA";
+
+ /**
+ * Name of algorithm for EC keys to be used when calling security provider
+ */
+ public static final String EC_ALGORITHM = "EC";
+
+ /**
+ * The {@link Set} of {@link PosixFilePermission} <U>not</U> allowed if strict
+ * permissions are enforced on key files
+ */
+ public static final Set<PosixFilePermission> STRICTLY_PROHIBITED_FILE_PERMISSION =
+ Collections.unmodifiableSet(
+ EnumSet.of(PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE, PosixFilePermission.GROUP_EXECUTE,
+ PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_WRITE, PosixFilePermission.OTHERS_EXECUTE));
+
+ /**
+ * System property that can be used to control the default fingerprint factory used for keys.
+ * If not set the {@link #DEFAULT_FINGERPRINT_DIGEST_FACTORY} is used
+ */
+ public static final String KEY_FINGERPRINT_FACTORY_PROP = "org.apache.sshd.keyFingerprintFactory";
+
+ /**
+ * The default {@link Factory} of {@link Digest}s initialized
+ * as the value of {@link #getDefaultFingerPrintFactory()} if not
+ * overridden by {@link #KEY_FINGERPRINT_FACTORY_PROP} or
+ * {@link #setDefaultFingerPrintFactory(DigestFactory)}
+ */
+ public static final DigestFactory DEFAULT_FINGERPRINT_DIGEST_FACTORY = BuiltinDigests.sha256;
+
+ private static final AtomicReference<DigestFactory> DEFAULT_DIGEST_HOLDER = new AtomicReference<>();
+
+ private static final Map<String, PublicKeyEntryDecoder<?, ?>> BY_KEY_TYPE_DECODERS_MAP =
+ new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+ private static final Map<Class<?>, PublicKeyEntryDecoder<?, ?>> BY_KEY_CLASS_DECODERS_MAP =
+ new HashMap<>();
+
+ static {
+ registerPublicKeyEntryDecoder(RSAPublicKeyDecoder.INSTANCE);
+ registerPublicKeyEntryDecoder(DSSPublicKeyEntryDecoder.INSTANCE);
+
+ if (SecurityUtils.isECCSupported()) {
+ registerPublicKeyEntryDecoder(ECDSAPublicKeyEntryDecoder.INSTANCE);
+ }
+ if (SecurityUtils.isEDDSACurveSupported()) {
+ registerPublicKeyEntryDecoder(SecurityUtils.getEDDSAPublicKeyEntryDecoder());
+ }
+ }
+
+ private KeyUtils() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ /**
+ * <P>Checks if a path has strict permissions</P>
+ * <UL>
+ * <LI><P>
+ * The path may not have {@link PosixFilePermission#OTHERS_EXECUTE}
+ * permission
+ * </P></LI>
+ *
+ * <LI><P>
+ * (For {@code Unix}) The path may not have group or others permissions
+ * </P></LI>
+ *
+ * <LI><P>
+ * (For {@code Unix}) If the path is a file, then its folder may not have
+ * group or others permissions
+ * </P></LI>
+ *
+ * <LI><P>
+ * The path must be owned by current user.
+ * </P></LI>
+ *
+ * <LI><P>
+ * (For {@code Unix}) The path may be owned by root.
+ * </P></LI>
+ *
+ * <LI><P>
+ * (For {@code Unix}) If the path is a file, then its folder must also
+ * have valid owner.
+ * </P></LI>
+ *
+ * </UL>
+ *
+ * @param path The {@link Path} to be checked - ignored if {@code null}
+ * or does not exist
+ * @param options The {@link LinkOption}s to use to query the file's permissions
+ * @return The violated permission as {@link SimpleImmutableEntry} where key is a message and
+ * value is the offending object {@link PosixFilePermission} or {@link String} for owner - {@code null}
+ * if no violations detected
+ * @throws IOException If failed to retrieve the permissions
+ * @see #STRICTLY_PROHIBITED_FILE_PERMISSION
+ */
+ public static SimpleImmutableEntry<String, Object> validateStrictKeyFilePermissions(Path path, LinkOption... options) throws IOException {
+ if ((path == null) || (!Files.exists(path, options))) {
+ return null;
+ }
+
+ Collection<PosixFilePermission> perms = IoUtils.getPermissions(path, options);
+ if (GenericUtils.isEmpty(perms)) {
+ return null;
+ }
+
+ if (perms.contains(PosixFilePermission.OTHERS_EXECUTE)) {
+ PosixFilePermission p = PosixFilePermission.OTHERS_EXECUTE;
+ return new SimpleImmutableEntry<>(String.format("Permissions violation (%s)", p), p);
+ }
+
+ if (OsUtils.isUNIX()) {
+ PosixFilePermission p = IoUtils.validateExcludedPermissions(perms, STRICTLY_PROHIBITED_FILE_PERMISSION);
+ if (p != null) {
+ return new SimpleImmutableEntry<>(String.format("Permissions violation (%s)", p), p);
+ }
+
+ if (Files.isRegularFile(path, options)) {
+ Path parent = path.getParent();
+ p = IoUtils.validateExcludedPermissions(IoUtils.getPermissions(parent, options), STRICTLY_PROHIBITED_FILE_PERMISSION);
+ if (p != null) {
+ return new SimpleImmutableEntry<>(String.format("Parent permissions violation (%s)", p), p);
+ }
+ }
+ }
+
+ String owner = IoUtils.getFileOwner(path, options);
+ if (GenericUtils.isEmpty(owner)) {
+ // we cannot get owner
+ // general issue: jvm does not support permissions
+ // security issue: specific filesystem does not support permissions
+ return null;
+ }
+
+ String current = OsUtils.getCurrentUser();
+ Set<String> expected = new HashSet<>();
+ expected.add(current);
+ if (OsUtils.isUNIX()) {
+ // Windows "Administrator" was considered however in Windows most likely a group is used.
+ expected.add(OsUtils.ROOT_USER);
+ }
+
+ if (!expected.contains(owner)) {
+ return new SimpleImmutableEntry<>(String.format("Owner violation (%s)", owner), owner);
+ }
+
+ if (OsUtils.isUNIX()) {
+ if (Files.isRegularFile(path, options)) {
+ String parentOwner = IoUtils.getFileOwner(path.getParent(), options);
+ if ((!GenericUtils.isEmpty(parentOwner)) && (!expected.contains(parentOwner))) {
+ return new SimpleImmutableEntry<>(String.format("Parent owner violation (%s)", parentOwner), parentOwner);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param keyType The key type - {@code OpenSSH} name - e.g., {@code ssh-rsa, ssh-dss}
+ * @param keySize The key size (in bits)
+ * @return A {@link KeyPair} of the specified type and size
+ * @throws GeneralSecurityException If failed to generate the key pair
+ * @see #getPublicKeyEntryDecoder(String)
+ * @see PublicKeyEntryDecoder#generateKeyPair(int)
+ */
+ public static KeyPair generateKeyPair(String keyType, int keySize) throws GeneralSecurityException {
+ PublicKeyEntryDecoder<?, ?> decoder = getPublicKeyEntryDecoder(keyType);
+ if (decoder == null) {
+ throw new InvalidKeySpecException("No decoder for key type=" + keyType);
+ }
+
+ return decoder.generateKeyPair(keySize);
+ }
+
+ /**
+ * Performs a deep-clone of the original {@link KeyPair} - i.e., creates
+ * <U>new</U> public/private keys that are clones of the original one
+ *
+ * @param keyType The key type - {@code OpenSSH} name - e.g., {@code ssh-rsa, ssh-dss}
+ * @param kp The {@link KeyPair} to clone - ignored if {@code null}
+ * @return The cloned instance
+ * @throws GeneralSecurityException If failed to clone the pair
+ */
+ public static KeyPair cloneKeyPair(String keyType, KeyPair kp) throws GeneralSecurityException {
+ PublicKeyEntryDecoder<?, ?> decoder = getPublicKeyEntryDecoder(keyType);
+ if (decoder == null) {
+ throw new InvalidKeySpecException("No decoder for key type=" + keyType);
+ }
+
+ return decoder.cloneKeyPair(kp);
+ }
+
+ /**
+ * @param decoder The decoder to register
+ * @throws IllegalArgumentException if no decoder or not key type or no
+ * supported names for the decoder
+ * @see PublicKeyEntryDecoder#getPublicKeyType()
+ * @see PublicKeyEntryDecoder#getSupportedTypeNames()
+ */
+ public static void registerPublicKeyEntryDecoder(PublicKeyEntryDecoder<?, ?> decoder) {
+ Objects.requireNonNull(decoder, "No decoder specified");
+
+ Class<?> pubType = Objects.requireNonNull(decoder.getPublicKeyType(), "No public key type declared");
+ Class<?> prvType = Objects.requireNonNull(decoder.getPrivateKeyType(), "No private key type declared");
+ synchronized (BY_KEY_CLASS_DECODERS_MAP) {
+ BY_KEY_CLASS_DECODERS_MAP.put(pubType, decoder);
+ BY_KEY_CLASS_DECODERS_MAP.put(prvType, decoder);
+ }
+
+ Collection<String> names = ValidateUtils.checkNotNullAndNotEmpty(decoder.getSupportedTypeNames(), "No supported key type");
+ synchronized (BY_KEY_TYPE_DECODERS_MAP) {
+ for (String n : names) {
+ PublicKeyEntryDecoder<?, ?> prev = BY_KEY_TYPE_DECODERS_MAP.put(n, decoder);
+ if (prev != null) {
+ //noinspection UnnecessaryContinue
+ continue; // debug breakpoint
+ }
+ }
+ }
+ }
+
+ /**
+ * @param keyType The {@code OpenSSH} key type string - e.g., {@code ssh-rsa, ssh-dss}
+ * - ignored if {@code null}/empty
+ * @return The registered {@link PublicKeyEntryDecoder} or {code null} if not found
+ */
+ public static PublicKeyEntryDecoder<?, ?> getPublicKeyEntryDecoder(String keyType) {
+ if (GenericUtils.isEmpty(keyType)) {
+ return null;
+ }
+
+ synchronized (BY_KEY_TYPE_DECODERS_MAP) {
+ return BY_KEY_TYPE_DECODERS_MAP.get(keyType);
+ }
+ }
+
+ /**
+ * @param kp The {@link KeyPair} to examine - ignored if {@code null}
+ * @return The matching {@link PublicKeyEntryDecoder} provided <U>both</U>
+ * the public and private keys have the same decoder - {@code null} if no
+ * match found
+ * @see #getPublicKeyEntryDecoder(Key)
+ */
+ public static PublicKeyEntryDecoder<?, ?> getPublicKeyEntryDecoder(KeyPair kp) {
+ if (kp == null) {
+ return null;
+ }
+
+ PublicKeyEntryDecoder<?, ?> d1 = getPublicKeyEntryDecoder(kp.getPublic());
+ PublicKeyEntryDecoder<?, ?> d2 = getPublicKeyEntryDecoder(kp.getPrivate());
+ if (d1 == d2) {
+ return d1;
+ } else {
+ return null; // some kind of mixed keys...
+ }
+ }
+
+ /**
+ * @param key The {@link Key} (public or private) - ignored if {@code null}
+ * @return The registered {@link PublicKeyEntryDecoder} for this key or {code null} if no match found
+ * @see #getPublicKeyEntryDecoder(Class)
+ */
+ public static PublicKeyEntryDecoder<?, ?> getPublicKeyEntryDecoder(Key key) {
+ if (key == null) {
+ return null;
+ } else {
+ return getPublicKeyEntryDecoder(key.getClass());
+ }
+ }
+
+ /**
+ * @param keyType The key {@link Class} - ignored if {@code null} or not a {@link Key}
+ * compatible type
+ * @return The registered {@link PublicKeyEntryDecoder} or {code null} if no match found
+ */
+ public static PublicKeyEntryDecoder<?, ?> getPublicKeyEntryDecoder(Class<?> keyType) {
+ if ((keyType == null) || (!Key.class.isAssignableFrom(keyType))) {
+ return null;
+ }
+
+ synchronized (BY_KEY_TYPE_DECODERS_MAP) {
+ PublicKeyEntryDecoder<?, ?> decoder = BY_KEY_CLASS_DECODERS_MAP.get(keyType);
+ if (decoder != null) {
+ return decoder;
+ }
+
+ // in case it is a derived class
+ for (PublicKeyEntryDecoder<?, ?> dec : BY_KEY_CLASS_DECODERS_MAP.values()) {
+ Class<?> pubType = dec.getPublicKeyType();
+ Class<?> prvType = dec.getPrivateKeyType();
+ if (pubType.isAssignableFrom(keyType) || prvType.isAssignableFrom(keyType)) {
+ return dec;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @return The default {@link DigestFactory}
+ * by the {@link #getFingerPrint(PublicKey)} and {@link #getFingerPrint(String)}
+ * methods
+ * @see #KEY_FINGERPRINT_FACTORY_PROP
+ * @see #setDefaultFingerPrintFactory(DigestFactory)
+ */
+ public static DigestFactory getDefaultFingerPrintFactory() {
+ DigestFactory factory = null;
+ synchronized (DEFAULT_DIGEST_HOLDER) {
+ factory = DEFAULT_DIGEST_HOLDER.get();
+ if (factory != null) {
+ return factory;
+ }
+
+ String propVal = System.getProperty(KEY_FINGERPRINT_FACTORY_PROP);
+ if (GenericUtils.isEmpty(propVal)) {
+ factory = DEFAULT_FINGERPRINT_DIGEST_FACTORY;
+ } else {
+ factory = ValidateUtils.checkNotNull(BuiltinDigests.fromFactoryName(propVal), "Unknown digest factory: %s", propVal);
+ }
+
+ ValidateUtils.checkTrue(factory.isSupported(), "Selected fingerprint digest not supported: %s", factory.getName());
+ DEFAULT_DIGEST_HOLDER.set(factory);
+ }
+
+ return factory;
+ }
+
+ /**
+ * @param f The {@link DigestFactory} of {@link Digest}s to be used - may
+ * not be {@code null}
+ */
+ public static void setDefaultFingerPrintFactory(DigestFactory f) {
+ synchronized (DEFAULT_DIGEST_HOLDER) {
+ DEFAULT_DIGEST_HOLDER.set(Objects.requireNonNull(f, "No digest factory"));
+ }
+ }
+
+ /**
+ * @param key the public key - ignored if {@code null}
+ * @return the fingerprint or {@code null} if no key.
+ * <B>Note:</B> if exception encountered then returns the exception's simple class name
+ * @see #getFingerPrint(Factory, PublicKey)
+ */
+ public static String getFingerPrint(PublicKey key) {
+ return getFingerPrint(getDefaultFingerPrintFactory(), key);
+ }
+
+ /**
+ * @param password The {@link String} to digest - ignored if {@code null}/empty,
+ * otherwise its UTF-8 representation is used as input for the fingerprint
+ * @return The fingerprint - {@code null} if {@code null}/empty input.
+ * <B>Note:</B> if exception encountered then returns the exception's simple class name
+ * @see #getFingerPrint(String, Charset)
+ */
+ public static String getFingerPrint(String password) {
+ return getFingerPrint(password, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * @param password The {@link String} to digest - ignored if {@code null}/empty
+ * @param charset The {@link Charset} to use in order to convert the
+ * string to its byte representation to use as input for the fingerprint
+ * @return The fingerprint - {@code null} if {@code null}/empty input.
+ * <B>Note:</B> if exception encountered then returns the exception's simple class name
+ * @see #getFingerPrint(Factory, String, Charset)
+ * @see #getDefaultFingerPrintFactory()
+ */
+ public static String getFingerPrint(String password, Charset charset) {
+ return getFingerPrint(getDefaultFingerPrintFactory(), password, charset);
+ }
+
+ /**
+ * @param f The {@link Factory} to create the {@link Digest} to use
+ * @param key the public key - ignored if {@code null}
+ * @return the fingerprint or {@code null} if no key.
+ * <B>Note:</B> if exception encountered then returns the exception's simple class name
+ * @see #getFingerPrint(Digest, PublicKey)
+ */
+ public static String getFingerPrint(Factory<? extends Digest> f, PublicKey key) {
+ return (key == null) ? null : getFingerPrint(Objects.requireNonNull(f, "No digest factory").create(), key);
+ }
+
+ /**
+ * @param d The {@link Digest} to use
+ * @param key the public key - ignored if {@code null}
+ * @return the fingerprint or {@code null} if no key.
+ * <B>Note:</B> if exception encountered then returns the exception's simple class name
+ * @see DigestUtils#getFingerPrint(Digest, byte[], int, int)
+ */
+ public static String getFingerPrint(Digest d, PublicKey key) {
+ if (key == null) {
+ return null;
+ }
+
+ try {
+ Buffer buffer = new ByteArrayBuffer();
+ buffer.putRawPublicKey(key);
+ return DigestUtils.getFingerPrint(d, buffer.array(), 0, buffer.wpos());
+ } catch (Exception e) {
+ return e.getClass().getSimpleName();
+ }
+ }
+
+ public static byte[] getRawFingerprint(PublicKey key) throws Exception {
+ return getRawFingerprint(getDefaultFingerPrintFactory(), key);
+ }
+
+ public static byte[] getRawFingerprint(Factory<? extends Digest> f, PublicKey key) throws Exception {
+ return (key == null) ? null : getRawFingerprint(Objects.requireNonNull(f, "No digest factory").create(), key);
+ }
+
+ public static byte[] getRawFingerprint(Digest d, PublicKey key) throws Exception {
+ if (key == null) {
+ return null;
+ }
+
+ Buffer buffer = new ByteArrayBuffer();
+ buffer.putRawPublicKey(key);
+ return DigestUtils.getRawFingerprint(d, buffer.array(), 0, buffer.wpos());
+ }
+
+ /**
+ * @param f The {@link Factory} to create the {@link Digest} to use
+ * @param s The {@link String} to digest - ignored if {@code null}/empty,
+ * otherwise its UTF-8 representation is used as input for the fingerprint
+ * @return The fingerprint - {@code null} if {@code null}/empty input.
+ * <B>Note:</B> if exception encountered then returns the exception's simple class name
+ * @see #getFingerPrint(Digest, String, Charset)
+ */
+ public static String getFingerPrint(Factory<? extends Digest> f, String s) {
+ return getFingerPrint(f, s, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * @param f The {@link Factory} to create the {@link Digest} to use
+ * @param s The {@link String} to digest - ignored if {@code null}/empty
+ * @param charset The {@link Charset} to use in order to convert the
+ * string to its byte representation to use as input for the fingerprint
+ * @return The fingerprint - {@code null} if {@code null}/empty input
+ * <B>Note:</B> if exception encountered then returns the exception's simple class name
+ * @see DigestUtils#getFingerPrint(Digest, String, Charset)
+ */
+ public static String getFingerPrint(Factory<? extends Digest> f, String s, Charset charset) {
+ return getFingerPrint(f.create(), s, charset);
+ }
+
+ /**
+ * @param d The {@link Digest} to use
+ * @param s The {@link String} to digest - ignored if {@code null}/empty,
+ * otherwise its UTF-8 representation is used as input for the fingerprint
+ * @return The fingerprint - {@code null} if {@code null}/empty input.
+ * <B>Note:</B> if exception encountered then returns the exception's simple class name
+ * @see DigestUtils#getFingerPrint(Digest, String, Charset)
+ */
+ public static String getFingerPrint(Digest d, String s) {
+ return getFingerPrint(d, s, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * @param d The {@link Digest} to use to calculate the fingerprint
+ * @param s The string to digest - ignored if {@code null}/empty
+ * @param charset The {@link Charset} to use in order to convert the
+ * string to its byte representation to use as input for the fingerprint
+ * @return The fingerprint - {@code null} if {@code null}/empty input.
+ * <B>Note:</B> if exception encountered then returns the exception's simple class name
+ * @see DigestUtils#getFingerPrint(Digest, String, Charset)
+ */
+ public static String getFingerPrint(Digest d, String s, Charset charset) {
+ if (GenericUtils.isEmpty(s)) {
+ return null;
+ }
+
+ try {
+ return DigestUtils.getFingerPrint(d, s, charset);
+ } catch (Exception e) {
+ return e.getClass().getSimpleName();
+ }
+ }
+
+ /**
+ * @param expected The expected fingerprint if {@code null} or empty then returns a failure
+ * with the default fingerprint.
+ * @param key the {@link PublicKey} - if {@code null} then returns null.
+ * @return SimpleImmutableEntry<Boolean, String> - key is success indicator, value is actual fingerprint,
+ * {@code null} if no key.
+ * @see #getDefaultFingerPrintFactory()
+ * @see #checkFingerPrint(String, Factory, PublicKey)
+ */
+ public static SimpleImmutableEntry<Boolean, String> checkFingerPrint(String expected, PublicKey key) {
+ return checkFingerPrint(expected, getDefaultFingerPrintFactory(), key);
+ }
+
+ /**
+ * @param expected The expected fingerprint if {@code null} or empty then returns a failure
+ * with the default fingerprint.
+ * @param f The {@link Factory} to be used to generate the default {@link Digest} for the key
+ * @param key the {@link PublicKey} - if {@code null} then returns null.
+ * @return SimpleImmutableEntry<Boolean, String> - key is success indicator, value is actual fingerprint,
+ * {@code null} if no key.
+ */
+ public static SimpleImmutableEntry<Boolean, String> checkFingerPrint(String expected, Factory<? extends Digest> f, PublicKey key) {
+ return checkFingerPrint(expected, Objects.requireNonNull(f, "No digest factory").create(), key);
+ }
+
+ /**
+ * @param expected The expected fingerprint if {@code null} or empty then returns a failure
+ * with the default fingerprint.
+ * @param d The {@link Digest} to be used to generate the default fingerprint for the key
+ * @param key the {@link PublicKey} - if {@code null} then returns null.
+ * @return SimpleImmutableEntry<Boolean, String> - key is success indicator, value is actual fingerprint,
+ * {@code null} if no key.
+ */
+ public static SimpleImmutableEntry<Boolean, String> checkFingerPrint(String expected, Digest d, PublicKey key) {
+ if (key == null) {
+ return null;
+ }
+
+ if (GenericUtils.isEmpty(expected)) {
+ return new SimpleImmutableEntry<>(false, getFingerPrint(d, key));
+ }
+
+ // de-construct fingerprint
+ int pos = expected.indexOf(':');
+ if ((pos < 0) || (pos >= (expected.length() - 1))) {
+ return new SimpleImmutableEntry<>(false, getFingerPrint(d, key));
+ }
+
+ String name = expected.substring(0, pos);
+ String value = expected.substring(pos + 1);
+ DigestFactory expectedFactory;
+ // We know that all digest names have a length > 2 - if 2 (or less) then assume a pure HEX value
+ if (name.length() > 2) {
+ expectedFactory = BuiltinDigests.fromFactoryName(name);
+ if (expectedFactory == null) {
+ return new SimpleImmutableEntry<>(false, getFingerPrint(d, key));
+ }
+
+ expected = name.toUpperCase() + ":" + value;
+ } else {
+ expectedFactory = BuiltinDigests.md5;
+ expected = expectedFactory.getName().toUpperCase() + ":" + expected;
+ }
+
+ String fingerprint = getFingerPrint(expectedFactory, key);
+ boolean matches = BuiltinDigests.md5.getName().equals(expectedFactory.getName())
+ ? expected.equalsIgnoreCase(fingerprint) // HEX is case insensitive
+ : expected.equals(fingerprint);
+ return new SimpleImmutableEntry<>(matches, fingerprint);
+ }
+
+ /**
+ * @param kp a key pair - ignored if {@code null}. If the private
+ * key is non-{@code null} then it is used to determine the type,
+ * otherwise the public one is used.
+ * @return the key type or {@code null} if cannot determine it
+ * @see #getKeyType(Key)
+ */
+ public static String getKeyType(KeyPair kp) {
+ if (kp == null) {
+ return null;
+ }
+ PrivateKey key = kp.getPrivate();
+ if (key != null) {
+ return getKeyType(key);
+ } else {
+ return getKeyType(kp.getPublic());
+ }
+ }
+
+ /**
+ * @param key a public or private key
+ * @return the key type or {@code null} if cannot determine it
+ */
+ public static String getKeyType(Key key) {
+ if (key == null) {
+ return null;
+ } else if (key instanceof DSAKey) {
+ return KeyPairProvider.SSH_DSS;
+ } else if (key instanceof RSAKey) {
+ return KeyPairProvider.SSH_RSA;
+ } else if (key instanceof ECKey) {
+ ECKey ecKey = (ECKey) key;
+ ECParameterSpec ecSpec = ecKey.getParams();
+ ECCurves curve = ECCurves.fromCurveParameters(ecSpec);
+ if (curve == null) {
+ return null; // debug breakpoint
+ } else {
+ return curve.getKeyType();
+ }
+ } else if (SecurityUtils.EDDSA.equalsIgnoreCase(key.getAlgorithm())) {
+ return KeyPairProvider.SSH_ED25519;
+ }
+
+ return null;
+ }
+
+ /**
+ * Determines the key size in bits
+ *
+ * @param key The {@link Key} to examine - ignored if {@code null}
+ * @return The key size - non-positive value if cannot determine it
+ */
+ public static int getKeySize(Key key) {
+ if (key == null) {
+ return -1;
+ } else if (key instanceof RSAKey) {
+ BigInteger n = ((RSAKey) key).getModulus();
+ return n.bitLength();
+ } else if (key instanceof DSAKey) {
+ DSAParams params = ((DSAKey) key).getParams();
+ BigInteger p = params.getP();
+ return p.bitLength();
+ } else if (key instanceof ECKey) {
+ ECParameterSpec ecSpec = ((ECKey) key).getParams();
+ ECCurves curve = ECCurves.fromCurveParameters(ecSpec);
+ if (curve != null) {
+ return curve.getKeySize();
+ }
+ } else if (SecurityUtils.EDDSA.equalsIgnoreCase(key.getAlgorithm())) {
+ return SecurityUtils.getEDDSAKeySize(key);
+ }
+
+ return -1;
+ }
+
+ /**
+ * @param key The {@link PublicKey} to be checked - ignored if {@code null}
+ * @param keySet The keys to be searched - ignored if {@code null}/empty
+ * @return The matching {@link PublicKey} from the keys or {@code null} if
+ * no match found
+ * @see #compareKeys(PublicKey, PublicKey)
+ */
+ public static PublicKey findMatchingKey(PublicKey key, PublicKey... keySet) {
+ if (key == null || GenericUtils.isEmpty(keySet)) {
+ return null;
+ } else {
+ return findMatchingKey(key, Arrays.asList(keySet));
+ }
+ }
+
+ /**
+ * @param key The {@link PublicKey} to be checked - ignored if {@code null}
+ * @param keySet The keys to be searched - ignored if {@code null}/empty
+ * @return The matching {@link PublicKey} from the keys or {@code null} if
+ * no match found
+ * @see #compareKeys(PublicKey, PublicKey)
+ */
+ public static PublicKey findMatchingKey(PublicKey key, Collection<? extends PublicKey> keySet) {
+ if (key == null || GenericUtils.isEmpty(keySet)) {
+ return null;
+ }
+ for (PublicKey k : keySet) {
+ if (compareKeys(key, k)) {
+ return k;
+ }
+ }
+ return null;
+ }
+
+ public static boolean compareKeyPairs(KeyPair k1, KeyPair k2) {
+ if (Objects.equals(k1, k2)) {
+ return true;
+ } else if ((k1 == null) || (k2 == null)) {
+ return false; // both null is covered by Objects#equals
+ } else {
+ return compareKeys(k1.getPublic(), k2.getPublic())
+ && compareKeys(k1.getPrivate(), k2.getPrivate());
+ }
+ }
+
+ public static boolean compareKeys(PublicKey k1, PublicKey k2) {
+ if ((k1 instanceof RSAPublicKey) && (k2 instanceof RSAPublicKey)) {
+ return compareRSAKeys(RSAPublicKey.class.cast(k1), RSAPublicKey.class.cast(k2));
+ } else if ((k1 instanceof DSAPublicKey) && (k2 instanceof DSAPublicKey)) {
+ return compareDSAKeys(DSAPublicKey.class.cast(k1), DSAPublicKey.class.cast(k2));
+ } else if ((k1 instanceof ECPublicKey) && (k2 instanceof ECPublicKey)) {
+ return compareECKeys(ECPublicKey.class.cast(k1), ECPublicKey.class.cast(k2));
+ } else if ((k1 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k1.getAlgorithm())
+ && (k2 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k2.getAlgorithm())) {
+ return SecurityUtils.compareEDDSAPPublicKeys(k1, k2);
+ } else {
+ return false; // either key is null or not of same class
+ }
+ }
+
+ public static PublicKey recoverPublicKey(PrivateKey key) throws GeneralSecurityException {
+ if (key instanceof RSAPrivateKey) {
+ return recoverRSAPublicKey((RSAPrivateKey) key);
+ } else if (key instanceof DSAPrivateKey) {
+ return recoverDSAPublicKey((DSAPrivateKey) key);
+ } else if ((key != null) && SecurityUtils.EDDSA.equalsIgnoreCase(key.getAlgorithm())) {
+ return SecurityUtils.recoverEDDSAPublicKey(key);
+ } else {
+ return null;
+ }
+ }
+
+ public static boolean compareKeys(PrivateKey k1, PrivateKey k2) {
+ if ((k1 instanceof RSAPrivateKey) && (k2 instanceof RSAPrivateKey)) {
+ return compareRSAKeys(RSAPrivateKey.class.cast(k1), RSAPrivateKey.class.cast(k2));
+ } else if ((k1 instanceof DSAPrivateKey) && (k2 instanceof DSAPrivateKey)) {
+ return compareDSAKeys(DSAPrivateKey.class.cast(k1), DSAPrivateKey.class.cast(k2));
+ } else if ((k1 instanceof ECPrivateKey) && (k2 instanceof ECPrivateKey)) {
+ return compareECKeys(ECPrivateKey.class.cast(k1), ECPrivateKey.class.cast(k2));
+ } else if ((k1 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k1.getAlgorithm())
+ && (k2 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k2.getAlgorithm())) {
+ return SecurityUtils.compareEDDSAPrivateKeys(k1, k2);
+ } else {
+ return false; // either key is null or not of same class
+ }
+ }
+
+ public static boolean compareRSAKeys(RSAPublicKey k1, RSAPublicKey k2) {
+ if (Objects.equals(k1, k2)) {
+ return true;
+ } else if (k1 == null || k2 == null) {
+ return false; // both null is covered by Objects#equals
+ } else {
+ return Objects.equals(k1.getPublicExponent(), k2.getPublicExponent())
+ && Objects.equals(k1.getModulus(), k2.getModulus());
+ }
+ }
+
+ public static boolean compareRSAKeys(RSAPrivateKey k1, RSAPrivateKey k2) {
+ if (Objects.equals(k1, k2)) {
+ return true;
+ } else if (k1 == null || k2 == null) {
+ return false; // both null is covered by Objects#equals
+ } else {
+ return Objects.equals(k1.getModulus(), k2.getModulus())
+ && Objects.equals(k1.getPrivateExponent(), k2.getPrivateExponent());
+ }
+ }
+
+ public static RSAPublicKey recoverRSAPublicKey(RSAPrivateKey privateKey) throws GeneralSecurityException {
+ if (privateKey instanceof RSAPrivateCrtKey) {
+ return recoverFromRSAPrivateCrtKey((RSAPrivateCrtKey) privateKey);
+ } else {
+ // Not ideal, but best we can do under the circumstances
+ return recoverRSAPublicKey(privateKey.getModulus(), DEFAULT_RSA_PUBLIC_EXPONENT);
+ }
+ }
+
+ public static RSAPublicKey recoverFromRSAPrivateCrtKey(RSAPrivateCrtKey rsaKey) throws GeneralSecurityException {
+ return recoverRSAPublicKey(rsaKey.getPrimeP(), rsaKey.getPrimeQ(), rsaKey.getPublicExponent());
+ }
+
+ public static RSAPublicKey recoverRSAPublicKey(BigInteger p, BigInteger q, BigInteger publicExponent) throws GeneralSecurityException {
+ return recoverRSAPublicKey(p.multiply(q), publicExponent);
+ }
+
+ public static RSAPublicKey recoverRSAPublicKey(BigInteger modulus, BigInteger publicExponent) throws GeneralSecurityException {
+ KeyFactory kf = SecurityUtils.getKeyFactory(RSA_ALGORITHM);
+ return (RSAPublicKey) kf.generatePublic(new RSAPublicKeySpec(modulus, publicExponent));
+ }
+
+ public static boolean compareDSAKeys(DSAPublicKey k1, DSAPublicKey k2) {
+ if (Objects.equals(k1, k2)) {
+ return true;
+ } else if (k1 == null || k2 == null) {
+ return false; // both null is covered by Objects#equals
+ } else {
+ return Objects.equals(k1.getY(), k2.getY())
+ && compareDSAParams(k1.getParams(), k2.getParams());
+ }
+ }
+
+ public static boolean compareDSAKeys(DSAPrivateKey k1, DSAPrivateKey k2) {
+ if (Objects.equals(k1, k2)) {
+ return true;
+ } else if (k1 == null || k2 == null) {
+ return false; // both null is covered by Objects#equals
+ } else {
+ return Objects.equals(k1.getX(), k2.getX())
+ && compareDSAParams(k1.getParams(), k2.getParams());
+ }
+ }
+
+ public static boolean compareDSAParams(DSAParams p1, DSAParams p2) {
+ if (Objects.equals(p1, p2)) {
+ return true;
+ } else if (p1 == null || p2 == null) {
+ return false; // both null is covered by Objects#equals
+ } else {
+ return Objects.equals(p1.getG(), p2.getG())
+ && Objects.equals(p1.getP(), p2.getP())
+ && Objects.equals(p1.getQ(), p2.getQ());
+ }
+ }
+
+ // based on code from https://github.com/alexo/SAML-2.0/blob/master/java-opensaml/opensaml-security-api/src/main/java/org/opensaml/xml/security/SecurityHelper.java
+ public static DSAPublicKey recoverDSAPublicKey(DSAPrivateKey privateKey) throws GeneralSecurityException {
+ DSAParams keyParams = privateKey.getParams();
+ BigInteger p = keyParams.getP();
+ BigInteger x = privateKey.getX();
+ BigInteger q = keyParams.getQ();
+ BigInteger g = keyParams.getG();
+ BigInteger y = g.modPow(x, p);
+ KeyFactory kf = SecurityUtils.getKeyFactory(DSS_ALGORITHM);
+ return (DSAPublicKey) kf.generatePublic(new DSAPublicKeySpec(y, p, q, g));
+ }
+
+ public static boolean compareECKeys(ECPrivateKey k1, ECPrivateKey k2) {
+ if (Objects.equals(k1, k2)) {
+ return true;
+ } else if (k1 == null || k2 == null) {
+ return false; // both null is covered by Objects#equals
+ } else {
+ return Objects.equals(k1.getS(), k2.getS())
+ && compareECParams(k1.getParams(), k2.getParams());
+ }
+ }
+
+ public static boolean compareECKeys(ECPublicKey k1, ECPublicKey k2) {
+ if (Objects.equals(k1, k2)) {
+ return true;
+ } else if (k1 == null || k2 == null) {
+ return false; // both null is covered by Objects#equals
+ } else {
+ return Objects.equals(k1.getW(), k2.getW())
+ && compareECParams(k1.getParams(), k2.getParams());
+ }
+ }
+
+ public static boolean compareECParams(ECParameterSpec s1, ECParameterSpec s2) {
+ if (Objects.equals(s1, s2)) {
+ return true;
+ } else if (s1 == null || s2 == null) {
+ return false; // both null is covered by Objects#equals
+ } else {
+ return Objects.equals(s1.getOrder(), s2.getOrder())
+ && (s1.getCofactor() == s2.getCofactor())
+ && Objects.equals(s1.getGenerator(), s2.getGenerator())
+ && Objects.equals(s1.getCurve(), s2.getCurve());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
new file mode 100644
index 0000000..6dbeee9
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
@@ -0,0 +1,142 @@
+/*
+ * 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.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Collection;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @param <PUB> Type of {@link PublicKey}
+ * @param <PRV> Type of {@link PrivateKey}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface PrivateKeyEntryDecoder<PUB extends PublicKey, PRV extends PrivateKey>
+ extends KeyEntryResolver<PUB, PRV>, PrivateKeyEntryResolver {
+
+ @Override
+ default PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
+ ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type provided");
+ Collection<String> supported = getSupportedTypeNames();
+ if ((GenericUtils.size(supported) > 0) && supported.contains(keyType)) {
+ return decodePrivateKey(FilePasswordProvider.EMPTY, keyData);
+ }
+
+ throw new InvalidKeySpecException("resolve(" + keyType + ") not in listed supported types: " + supported);
+ }
+
+ /**
+ * @param passwordProvider The {@link FilePasswordProvider} to use
+ * in case the data is encrypted - may be {@code null} if no encrypted
+ * data is expected
+ * @param keyData The key data bytes in {@code OpenSSH} format (after
+ * BASE64 decoding) - ignored if {@code null}/empty
+ * @return The decoded {@link PrivateKey} - or {@code null} if no data
+ * @throws IOException If failed to decode the key
+ * @throws GeneralSecurityException If failed to generate the key
+ */
+ default PRV decodePrivateKey(FilePasswordProvider passwordProvider, byte... keyData)
+ throws IOException, GeneralSecurityException {
+ return decodePrivateKey(passwordProvider, keyData, 0, NumberUtils.length(keyData));
+ }
+
+ default PRV decodePrivateKey(FilePasswordProvider passwordProvider, byte[] keyData, int offset, int length)
+ throws IOException, GeneralSecurityException {
+ if (length <= 0) {
+ return null;
+ }
+
+ try (InputStream stream = new ByteArrayInputStream(keyData, offset, length)) {
+ return decodePrivateKey(passwordProvider, stream);
+ }
+ }
+
+ default PRV decodePrivateKey(FilePasswordProvider passwordProvider, InputStream keyData)
+ throws IOException, GeneralSecurityException {
+ // the actual data is preceded by a string that repeats the key type
+ String type = KeyEntryResolver.decodeString(keyData);
+ if (GenericUtils.isEmpty(type)) {
+ throw new StreamCorruptedException("Missing key type string");
+ }
+
+ Collection<String> supported = getSupportedTypeNames();
+ if (GenericUtils.isEmpty(supported) || (!supported.contains(type))) {
+ throw new InvalidKeySpecException("Reported key type (" + type + ") not in supported list: " + supported);
+ }
+
+ return decodePrivateKey(type, passwordProvider, keyData);
+ }
+
+ /**
+ * @param keyType The reported / encode key type
+ * @param passwordProvider The {@link FilePasswordProvider} to use
+ * in case the data is encrypted - may be {@code null} if no encrypted
+ * data is expected
+ * @param keyData The key data bytes stream positioned after the key type decoding
+ * and making sure it is one of the supported types
+ * @return The decoded {@link PrivateKey}
+ * @throws IOException If failed to read from the data stream
+ * @throws GeneralSecurityException If failed to generate the key
+ */
+ PRV decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
+ throws IOException, GeneralSecurityException;
+
+ /**
+ * Encodes the {@link PrivateKey} using the {@code OpenSSH} format - same
+ * one used by the {@code decodePublicKey} method(s)
+ *
+ * @param s The {@link OutputStream} to write the data to
+ * @param key The {@link PrivateKey} - may not be {@code null}
+ * @return The key type value - one of the {@link #getSupportedTypeNames()} or
+ * {@code null} if encoding not supported
+ * @throws IOException If failed to generate the encoding
+ */
+ default String encodePrivateKey(OutputStream s, PRV key) throws IOException {
+ Objects.requireNonNull(key, "No private key provided");
+ return null;
+ }
+
+ default boolean isPublicKeyRecoverySupported() {
+ return false;
+ }
+
+ /**
+ * Attempts to recover the public key given the private one
+ *
+ * @param prvKey The {@link PrivateKey}
+ * @return The recovered {@link PublicKey} - {@code null} if cannot recover it
+ * @throws GeneralSecurityException If failed to generate the public key
+ */
+ default PUB recoverPublicKey(PRV prvKey) throws GeneralSecurityException {
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java
new file mode 100644
index 0000000..1e4c91e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java
@@ -0,0 +1,70 @@
+/*
+ * 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.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.spec.InvalidKeySpecException;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface PrivateKeyEntryResolver {
+ /**
+ * A resolver that ignores all input
+ */
+ PrivateKeyEntryResolver IGNORING = new PrivateKeyEntryResolver() {
+ @Override
+ public PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "IGNORING";
+ }
+ };
+
+ /**
+ * A resolver that fails on all input
+ */
+ PrivateKeyEntryResolver FAILING = new PrivateKeyEntryResolver() {
+ @Override
+ public PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
+ throw new InvalidKeySpecException("Failing resolver on key type=" + keyType);
+ }
+
+ @Override
+ public String toString() {
+ return "FAILING";
+ }
+ };
+
+ /**
+ * @param keyType The {@code OpenSSH} reported key type
+ * @param keyData The {@code OpenSSH} encoded key data
+ * @return The extracted {@link PrivateKey} - ignored if {@code null}
+ * @throws IOException If failed to parse the key data
+ * @throws GeneralSecurityException If failed to generate the key
+ */
+ PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java
new file mode 100644
index 0000000..1db3d2b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java
@@ -0,0 +1,286 @@
+/*
+ * 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.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.StreamCorruptedException;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+
+/**
+ * <P>Represents a {@link PublicKey} whose data is formatted according to
+ * the <A HREF="http://en.wikibooks.org/wiki/OpenSSH">OpenSSH</A> format:</P>
+ *
+ * <PRE>
+ * <key-type> <base64-encoded-public-key-data>
+ * </PRE>
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class PublicKeyEntry implements Serializable {
+
+ /**
+ * Character used to denote a comment line in the keys file
+ */
+ public static final char COMMENT_CHAR = '#';
+
+
+ /**
+ * Standard folder name used by OpenSSH to hold key files
+ */
+ public static final String STD_KEYFILE_FOLDER_NAME = ".ssh";
+
+ private static final long serialVersionUID = -585506072687602760L;
+
+ private String keyType;
+ private byte[] keyData;
+
+ public PublicKeyEntry() {
+ super();
+ }
+
+ public PublicKeyEntry(String keyType, byte... keyData) {
+ this.keyType = keyType;
+ this.keyData = keyData;
+ }
+
+ public String getKeyType() {
+ return keyType;
+ }
+
+ public void setKeyType(String value) {
+ this.keyType = value;
+ }
+
+ public byte[] getKeyData() {
+ return keyData;
+ }
+
+ public void setKeyData(byte[] value) {
+ this.keyData = value;
+ }
+
+ /**
+ * @param fallbackResolver The {@link PublicKeyEntryResolver} to consult if
+ * none of the built-in ones can be used. If {@code null} and no built-in
+ * resolver can be used then an {@link InvalidKeySpecException} is thrown.
+ * @return The resolved {@link PublicKey} - or {@code null} if could not be
+ * resolved. <B>Note:</B> may be called only after key type and data bytes
+ * have been set or exception(s) may be thrown
+ * @throws IOException If failed to decode the key
+ * @throws GeneralSecurityException If failed to generate the key
+ */
+ public PublicKey resolvePublicKey(PublicKeyEntryResolver fallbackResolver) throws IOException, GeneralSecurityException {
+ String kt = getKeyType();
+ PublicKeyEntryResolver decoder = KeyUtils.getPublicKeyEntryDecoder(kt);
+ if (decoder == null) {
+ decoder = fallbackResolver;
+ }
+ if (decoder == null) {
+ throw new InvalidKeySpecException("No decoder available for key type=" + kt);
+ }
+
+ return decoder.resolve(kt, getKeyData());
+ }
+
+ /**
+ * @param sb The {@link Appendable} instance to encode the data into
+ * @param fallbackResolver The {@link PublicKeyEntryResolver} to consult if
+ * none of the built-in ones can be used. If {@code null} and no built-in
+ * resolver can be used then an {@link InvalidKeySpecException} is thrown.
+ * @return The {@link PublicKey} or {@code null} if could not resolve it
+ * @throws IOException If failed to decode/encode the key
+ * @throws GeneralSecurityException If failed to generate the key
+ * @see #resolvePublicKey(PublicKeyEntryResolver)
+ */
+ public PublicKey appendPublicKey(Appendable sb, PublicKeyEntryResolver fallbackResolver) throws IOException, GeneralSecurityException {
+ PublicKey key = resolvePublicKey(fallbackResolver);
+ if (key != null) {
+ appendPublicKeyEntry(sb, key);
+ }
+ return key;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(getKeyType()) + Arrays.hashCode(getKeyData());
+ }
+
+ /*
+ * In case some derived class wants to define some "extended" equality
+ * without having to repeat this code
+ */
+ protected boolean isEquivalent(PublicKeyEntry e) {
+ if (this == e) {
+ return true;
+ }
+ return Objects.equals(getKeyType(), e.getKeyType())
+ && Arrays.equals(getKeyData(), e.getKeyData());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ return isEquivalent((PublicKeyEntry) obj);
+ }
+
+ @Override
+ public String toString() {
+ byte[] data = getKeyData();
+ Base64.Encoder encoder = Base64.getEncoder();
+ return getKeyType() + " " + (NumberUtils.isEmpty(data) ? "<no-key>" : encoder.encodeToString(data));
+ }
+
+ /**
+ * @param encData Assumed to contain at least {@code key-type base64-data}
+ * (anything beyond the BASE64 data is ignored) - ignored if {@code null}/empty
+ * @return A {@link PublicKeyEntry} or {@code null} if no data
+ * @throws IllegalArgumentException if bad format found
+ * @see #parsePublicKeyEntry(PublicKeyEntry, String)
+ */
+ public static PublicKeyEntry parsePublicKeyEntry(String encData) throws IllegalArgumentException {
+ String data = GenericUtils.replaceWhitespaceAndTrim(encData);
+ if (GenericUtils.isEmpty(data)) {
+ return null;
+ } else {
+ return parsePublicKeyEntry(new PublicKeyEntry(), data);
+ }
+ }
+
+ /**
+ * @param <E> The generic entry type
+ * @param entry The {@link PublicKeyEntry} whose contents are to be
+ * updated - ignored if {@code null}
+ * @param encData Assumed to contain at least {@code key-type base64-data} (anything
+ * beyond the BASE64 data is ignored) - ignored if {@code null}/empty
+ * @return The updated entry instance
+ * @throws IllegalArgumentException if bad format found
+ */
+ public static <E extends PublicKeyEntry> E parsePublicKeyEntry(E entry, String encData) throws IllegalArgumentException {
+ String data = GenericUtils.replaceWhitespaceAndTrim(encData);
+ if (GenericUtils.isEmpty(data) || (entry == null)) {
+ return entry;
+ }
+
+ int startPos = data.indexOf(' ');
+ if (startPos <= 0) {
+ throw new IllegalArgumentException("Bad format (no key data delimiter): " + data);
+ }
+
+ int endPos = data.indexOf(' ', startPos + 1);
+ if (endPos <= startPos) { // OK if no continuation beyond the BASE64 encoded data
+ endPos = data.length();
+ }
+
+ String keyType = data.substring(0, startPos);
+ String b64Data = data.substring(startPos + 1, endPos).trim();
+ Base64.Decoder decoder = Base64.getDecoder();
+ byte[] keyData = decoder.decode(b64Data);
+ if (NumberUtils.isEmpty(keyData)) {
+ throw new IllegalArgumentException("Bad format (no BASE64 key data): " + data);
+ }
+
+ entry.setKeyType(keyType);
+ entry.setKeyData(keyData);
+ return entry;
+ }
+
+ /**
+ * @param key The {@link PublicKey}
+ * @return The {@code OpenSSH} encoded data
+ * @throws IllegalArgumentException If failed to encode
+ * @see #appendPublicKeyEntry(Appendable, PublicKey)
+ */
+ public static String toString(PublicKey key) throws IllegalArgumentException {
+ try {
+ return appendPublicKeyEntry(new StringBuilder(Byte.MAX_VALUE), key).toString();
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Failed (" + e.getClass().getSimpleName() + ") to encode: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Encodes a public key data the same way as the {@link #parsePublicKeyEntry(String)} expects it
+ *
+ * @param <A> The generic appendable class
+ * @param sb The {@link Appendable} instance to encode the data into
+ * @param key The {@link PublicKey} - ignored if {@code null}
+ * @return The updated appendable instance
+ * @throws IOException If failed to append the data
+ */
+ public static <A extends Appendable> A appendPublicKeyEntry(A sb, PublicKey key) throws IOException {
+ if (key == null) {
+ return sb;
+ }
+
+ @SuppressWarnings("unchecked")
+ PublicKeyEntryDecoder<PublicKey, ?> decoder =
+ (PublicKeyEntryDecoder<PublicKey, ?>) KeyUtils.getPublicKeyEntryDecoder(key);
+ if (decoder == null) {
+ throw new StreamCorruptedException("Cannot retrieve decoder for key=" + key.getAlgorithm());
+ }
+
+ try (ByteArrayOutputStream s = new ByteArrayOutputStream(Byte.MAX_VALUE)) {
+ String keyType = decoder.encodePublicKey(s, key);
+ byte[] bytes = s.toByteArray();
+ Base64.Encoder encoder = Base64.getEncoder();
+ String b64Data = encoder.encodeToString(bytes);
+ sb.append(keyType).append(' ').append(b64Data);
+ }
+
+ return sb;
+ }
+
+ private static final class LazyDefaultKeysFolderHolder {
+ private static final Path PATH =
+ IdentityUtils.getUserHomeFolder().resolve(STD_KEYFILE_FOLDER_NAME);
+
+ private LazyDefaultKeysFolderHolder() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+ }
+
+ /**
+ * @return The default OpenSSH folder used to hold key files - e.g.,
+ * {@code known_hosts}, {@code authorized_keys}, etc.
+ */
+ @SuppressWarnings("synthetic-access")
+ public static Path getDefaultKeysFolderPath() {
+ return LazyDefaultKeysFolderHolder.PATH;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
new file mode 100644
index 0000000..7462e4a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
@@ -0,0 +1,114 @@
+/*
+ * 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.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Collection;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Represents a decoder of an {@code OpenSSH} encoded key data
+ *
+ * @param <PUB> Type of {@link PublicKey}
+ * @param <PRV> Type of {@link PrivateKey}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface PublicKeyEntryDecoder<PUB extends PublicKey, PRV extends PrivateKey>
+ extends KeyEntryResolver<PUB, PRV>, PublicKeyEntryResolver {
+
+ @Override
+ default PublicKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
+ ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type provided");
+ Collection<String> supported = getSupportedTypeNames();
+ if ((GenericUtils.size(supported) > 0) && supported.contains(keyType)) {
+ return decodePublicKey(keyData);
+ }
+
+ throw new InvalidKeySpecException("resolve(" + keyType + ") not in listed supported types: " + supported);
+ }
+
+ /**
+ * @param keyData The key data bytes in {@code OpenSSH} format (after
+ * BASE64 decoding) - ignored if {@code null}/empty
+ * @return The decoded {@link PublicKey} - or {@code null} if no data
+ * @throws IOException If failed to decode the key
+ * @throws GeneralSecurityException If failed to generate the key
+ */
+ default PUB decodePublicKey(byte... keyData) throws IOException, GeneralSecurityException {
+ return decodePublicKey(keyData, 0, NumberUtils.length(keyData));
+ }
+
+ default PUB decodePublicKey(byte[] keyData, int offset, int length) throws IOException, GeneralSecurityException {
+ if (length <= 0) {
+ return null;
+ }
+
+ try (InputStream stream = new ByteArrayInputStream(keyData, offset, length)) {
+ return decodePublicKey(stream);
+ }
+ }
+
+ default PUB decodePublicKey(InputStream keyData) throws IOException, GeneralSecurityException {
+ // the actual data is preceded by a string that repeats the key type
+ String type = KeyEntryResolver.decodeString(keyData);
+ if (GenericUtils.isEmpty(type)) {
+ throw new StreamCorruptedException("Missing key type string");
+ }
+
+ Collection<String> supported = getSupportedTypeNames();
+ if (GenericUtils.isEmpty(supported) || (!supported.contains(type))) {
+ throw new InvalidKeySpecException("Reported key type (" + type + ") not in supported list: " + supported);
+ }
+
+ return decodePublicKey(type, keyData);
+ }
+
+ /**
+ * @param keyType The reported / encode key type
+ * @param keyData The key data bytes stream positioned after the key type decoding
+ * and making sure it is one of the supported types
+ * @return The decoded {@link PublicKey}
+ * @throws IOException If failed to read from the data stream
+ * @throws GeneralSecurityException If failed to generate the key
+ */
+ PUB decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException;
+
+ /**
+ * Encodes the {@link PublicKey} using the {@code OpenSSH} format - same
+ * one used by the {@code decodePublicKey} method(s)
+ *
+ * @param s The {@link OutputStream} to write the data to
+ * @param key The {@link PublicKey} - may not be {@code null}
+ * @return The key type value - one of the {@link #getSupportedTypeNames()}
+ * @throws IOException If failed to generate the encoding
+ */
+ String encodePublicKey(OutputStream s, PUB key) throws IOException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryResolver.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryResolver.java
new file mode 100644
index 0000000..e5eb0ed
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryResolver.java
@@ -0,0 +1,70 @@
+/*
+ * 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.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface PublicKeyEntryResolver {
+ /**
+ * A resolver that ignores all input
+ */
+ PublicKeyEntryResolver IGNORING = new PublicKeyEntryResolver() {
+ @Override
+ public PublicKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "IGNORING";
+ }
+ };
+
+ /**
+ * A resolver that fails on all input
+ */
+ PublicKeyEntryResolver FAILING = new PublicKeyEntryResolver() {
+ @Override
+ public PublicKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
+ throw new InvalidKeySpecException("Failing resolver on key type=" + keyType);
+ }
+
+ @Override
+ public String toString() {
+ return "FAILING";
+ }
+ };
+
+ /**
+ * @param keyType The {@code OpenSSH} reported key type
+ * @param keyData The {@code OpenSSH} encoded key data
+ * @return The extracted {@link PublicKey} - ignored if {@code null}
+ * @throws IOException If failed to parse the key data
+ * @throws GeneralSecurityException If failed to generate the key
+ */
+ PublicKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractIdentityResourceLoader.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractIdentityResourceLoader.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractIdentityResourceLoader.java
new file mode 100644
index 0000000..a2412bd
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractIdentityResourceLoader.java
@@ -0,0 +1,62 @@
+/*
+ * 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.impl;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Collection;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.IdentityResourceLoader;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * @param <PUB> Generic public key type
+ * @param <PRV> Generic private key type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractIdentityResourceLoader<PUB extends PublicKey, PRV extends PrivateKey>
+ extends AbstractLoggingBean
+ implements IdentityResourceLoader<PUB, PRV> {
+ private final Class<PUB> pubType;
+ private final Class<PRV> prvType;
+ private final Collection<String> names;
+
+ protected AbstractIdentityResourceLoader(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
+ this.pubType = Objects.requireNonNull(pubType, "No public key type specified");
+ this.prvType = Objects.requireNonNull(prvType, "No private key type specified");
+ this.names = ValidateUtils.checkNotNullAndNotEmpty(names, "No type names provided");
+ }
+
+ @Override
+ public final Class<PUB> getPublicKeyType() {
+ return pubType;
+ }
+
+ @Override
+ public final Class<PRV> getPrivateKeyType() {
+ return prvType;
+ }
+
+ @Override
+ public Collection<String> getSupportedTypeNames() {
+ return names;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.java
new file mode 100644
index 0000000..7afa3a6
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.java
@@ -0,0 +1,59 @@
+/*
+ * 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.impl;
+
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.KeySpec;
+import java.util.Collection;
+
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+
+/**
+ * @param <PUB> Type of {@link PublicKey}
+ * @param <PRV> Type of {@link PrivateKey}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractKeyEntryResolver<PUB extends PublicKey, PRV extends PrivateKey>
+ extends AbstractIdentityResourceLoader<PUB, PRV>
+ implements KeyEntryResolver<PUB, PRV> {
+ protected AbstractKeyEntryResolver(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
+ super(pubType, prvType, names);
+ }
+
+ public PUB generatePublicKey(KeySpec keySpec) throws GeneralSecurityException {
+ KeyFactory factory = getKeyFactoryInstance();
+ Class<PUB> keyType = getPublicKeyType();
+ return keyType.cast(factory.generatePublic(keySpec));
+ }
+
+ public PRV generatePrivateKey(KeySpec keySpec) throws GeneralSecurityException {
+ KeyFactory factory = getKeyFactoryInstance();
+ Class<PRV> keyType = getPrivateKeyType();
+ return keyType.cast(factory.generatePrivate(keySpec));
+ }
+
+ @Override
+ public String toString() {
+ return getPublicKeyType().getSimpleName() + ": " + getSupportedTypeNames();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPrivateKeyEntryDecoder.java
new file mode 100644
index 0000000..574412e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPrivateKeyEntryDecoder.java
@@ -0,0 +1,40 @@
+/*
+ * 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.impl;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Collection;
+
+import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
+
+/**
+ * @param <PUB> Type of {@link PublicKey}
+ * @param <PRV> Type of {@link PrivateKey}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractPrivateKeyEntryDecoder<PUB extends PublicKey, PRV extends PrivateKey>
+ extends AbstractKeyEntryResolver<PUB, PRV>
+ implements PrivateKeyEntryDecoder<PUB, PRV> {
+ protected AbstractPrivateKeyEntryDecoder(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
+ super(pubType, prvType, names);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPublicKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPublicKeyEntryDecoder.java
new file mode 100644
index 0000000..59cbf3a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractPublicKeyEntryDecoder.java
@@ -0,0 +1,41 @@
+/*
+ * 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.impl;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Collection;
+
+import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
+
+/**
+ * Useful base class implementation for a decoder of an {@code OpenSSH} encoded key data
+ *
+ * @param <PUB> Type of {@link PublicKey}
+ * @param <PRV> Type of {@link PrivateKey}
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractPublicKeyEntryDecoder<PUB extends PublicKey, PRV extends PrivateKey>
+ extends AbstractKeyEntryResolver<PUB, PRV>
+ implements PublicKeyEntryDecoder<PUB, PRV> {
+ protected AbstractPublicKeyEntryDecoder(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
+ super(pubType, prvType, names);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/DSSPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/DSSPublicKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/DSSPublicKeyEntryDecoder.java
new file mode 100644
index 0000000..464d2b0
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/DSSPublicKeyEntryDecoder.java
@@ -0,0 +1,119 @@
+/*
+ * 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.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DSSPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<DSAPublicKey, DSAPrivateKey> {
+ public static final DSSPublicKeyEntryDecoder INSTANCE = new DSSPublicKeyEntryDecoder();
+
+ public DSSPublicKeyEntryDecoder() {
+ super(DSAPublicKey.class, DSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_DSS)));
+ }
+
+ @Override
+ public DSAPublicKey decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException {
+ if (!KeyPairProvider.SSH_DSS.equals(keyType)) { // just in case we were invoked directly
+ throw new InvalidKeySpecException("Unexpected key type: " + keyType);
+ }
+
+ BigInteger p = KeyEntryResolver.decodeBigInt(keyData);
+ BigInteger q = KeyEntryResolver.decodeBigInt(keyData);
+ BigInteger g = KeyEntryResolver.decodeBigInt(keyData);
+ BigInteger y = KeyEntryResolver.decodeBigInt(keyData);
+
+ return generatePublicKey(new DSAPublicKeySpec(y, p, q, g));
+ }
+
+ @Override
+ public String encodePublicKey(OutputStream s, DSAPublicKey key) throws IOException {
+ Objects.requireNonNull(key, "No public key provided");
+
+ DSAParams keyParams = Objects.requireNonNull(key.getParams(), "No DSA params available");
+ KeyEntryResolver.encodeString(s, KeyPairProvider.SSH_DSS);
+ KeyEntryResolver.encodeBigInt(s, keyParams.getP());
+ KeyEntryResolver.encodeBigInt(s, keyParams.getQ());
+ KeyEntryResolver.encodeBigInt(s, keyParams.getG());
+ KeyEntryResolver.encodeBigInt(s, key.getY());
+
+ return KeyPairProvider.SSH_DSS;
+ }
+
+ @Override
+ public DSAPublicKey clonePublicKey(DSAPublicKey key) throws GeneralSecurityException {
+ if (key == null) {
+ return null;
+ }
+
+ DSAParams params = key.getParams();
+ if (params == null) {
+ throw new InvalidKeyException("Missing parameters in key");
+ }
+
+ return generatePublicKey(new DSAPublicKeySpec(key.getY(), params.getP(), params.getQ(), params.getG()));
+ }
+
+ @Override
+ public DSAPrivateKey clonePrivateKey(DSAPrivateKey key) throws GeneralSecurityException {
+ if (key == null) {
+ return null;
+ }
+
+ DSAParams params = key.getParams();
+ if (params == null) {
+ throw new InvalidKeyException("Missing parameters in key");
+ }
+
+ return generatePrivateKey(new DSAPrivateKeySpec(key.getX(), params.getP(), params.getQ(), params.getG()));
+ }
+
+ @Override
+ public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+ return SecurityUtils.getKeyPairGenerator(KeyUtils.DSS_ALGORITHM);
+ }
+
+ @Override
+ public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+ return SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
+ }
+}
[15/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
deleted file mode 100644
index c13cd9e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
+++ /dev/null
@@ -1,604 +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.util.buffer;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.StreamCorruptedException;
-import java.util.function.IntUnaryOperator;
-import java.util.logging.Level;
-
-import org.apache.sshd.common.PropertyResolver;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.logging.SimplifiedLog;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class BufferUtils {
- public static final char DEFAULT_HEX_SEPARATOR = ' ';
- public static final char EMPTY_HEX_SEPARATOR = '\0';
- public static final String HEX_DIGITS = "0123456789abcdef";
-
- public static final String HEXDUMP_CHUNK_SIZE = "sshd-hexdump-chunk-size";
- public static final int DEFAULT_HEXDUMP_CHUNK_SIZE = 64;
- public static final Level DEFAULT_HEXDUMP_LEVEL = Level.FINEST;
-
- public static final IntUnaryOperator DEFAULT_BUFFER_GROWTH_FACTOR = BufferUtils::getNextPowerOf2;
-
- /**
- * Maximum value of a {@code uint32} field
- */
- public static final long MAX_UINT32_VALUE = 0x0FFFFFFFFL;
-
- /**
- * Maximum value of a {@code uint8} field
- */
- public static final int MAX_UINT8_VALUE = 0x0FF;
-
- /**
- * Private Constructor
- */
- private BufferUtils() {
- throw new UnsupportedOperationException("No instance allowed");
- }
-
- public static void dumpHex(SimplifiedLog logger, Level level, String prefix, PropertyResolver resolver, char sep, byte... data) {
- dumpHex(logger, level, prefix, resolver, sep, data, 0, NumberUtils.length(data));
- }
-
- public static void dumpHex(SimplifiedLog logger, Level level, String prefix, PropertyResolver resolver, char sep, byte[] data, int offset, int len) {
- dumpHex(logger, level, prefix, sep, resolver.getIntProperty(HEXDUMP_CHUNK_SIZE, DEFAULT_HEXDUMP_CHUNK_SIZE), data, offset, len);
- }
-
- public static void dumpHex(SimplifiedLog logger, Level level, String prefix, char sep, int chunkSize, byte... data) {
- dumpHex(logger, level, prefix, sep, chunkSize, data, 0, NumberUtils.length(data));
- }
-
- public static void dumpHex(SimplifiedLog logger, Level level, String prefix, char sep, int chunkSize, byte[] data, int offset, int len) {
- if ((logger == null) || (level == null) || (!logger.isEnabled(level))) {
- return;
- }
-
- StringBuilder sb = new StringBuilder(chunkSize * 3 /* HEX */ + prefix.length() + Long.SIZE /* some extra */);
- sb.append(prefix);
- for (int remainLen = len, chunkIndex = 1, curOffset = offset, totalLen = 0; remainLen > 0; chunkIndex++) {
- sb.setLength(prefix.length()); // reset for next chunk
-
- sb.append(" [chunk #").append(chunkIndex).append(']');
-
- int dumpSize = Math.min(chunkSize, remainLen);
- totalLen += dumpSize;
- sb.append('(').append(totalLen).append('/').append(len).append(')');
-
- try {
- appendHex(sb.append(' '), data, curOffset, dumpSize, sep);
- } catch (IOException e) { // unexpected
- sb.append(e.getClass().getSimpleName()).append(": ").append(e.getMessage());
- }
-
- // Pad the last (incomplete) line to align its data view
- for (int index = dumpSize; index < chunkSize; index++) {
- if (sep != EMPTY_HEX_SEPARATOR) {
- sb.append(' ');
- }
- sb.append(" ");
- }
-
- sb.append(" ");
- for (int pos = curOffset, l = 0; l < dumpSize; pos++, l++) {
- int b = data[pos] & 0xFF;
- if ((b > ' ') && (b < 0x7E)) {
- sb.append((char) b);
- } else {
- sb.append('.');
- }
- }
-
- logger.log(level, sb.toString());
- remainLen -= dumpSize;
- curOffset += dumpSize;
- }
- }
-
- public static String toHex(byte... array) {
- return toHex(array, 0, NumberUtils.length(array));
- }
-
- public static String toHex(char sep, byte... array) {
- return toHex(array, 0, NumberUtils.length(array), sep);
- }
-
- public static String toHex(byte[] array, int offset, int len) {
- return toHex(array, offset, len, DEFAULT_HEX_SEPARATOR);
- }
-
- public static String toHex(byte[] array, int offset, int len, char sep) {
- if (len <= 0) {
- return "";
- }
-
- try {
- return appendHex(new StringBuilder(len * 3 /* 2 HEX + sep */), array, offset, len, sep).toString();
- } catch (IOException e) { // unexpected
- return e.getClass().getSimpleName() + ": " + e.getMessage();
- }
- }
-
- public static <A extends Appendable> A appendHex(A sb, char sep, byte... array) throws IOException {
- return appendHex(sb, array, 0, NumberUtils.length(array), sep);
- }
-
- public static <A extends Appendable> A appendHex(A sb, byte[] array, int offset, int len, char sep) throws IOException {
- if (len <= 0) {
- return sb;
- }
-
- for (int curOffset = offset, maxOffset = offset + len; curOffset < maxOffset; curOffset++) {
- byte b = array[curOffset];
- if ((curOffset > offset) && (sep != EMPTY_HEX_SEPARATOR)) {
- sb.append(sep);
- }
- sb.append(HEX_DIGITS.charAt((b >> 4) & 0x0F));
- sb.append(HEX_DIGITS.charAt(b & 0x0F));
- }
-
- return sb;
- }
-
- /**
- * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
- * @param csq The {@link CharSequence} containing the HEX encoded bytes
- * @return The decoded bytes
- * @throws IllegalArgumentException If invalid HEX sequence length
- * @throws NumberFormatException If invalid HEX characters found
- * @see #decodeHex(char, CharSequence, int, int)
- */
- public static byte[] decodeHex(char separator, CharSequence csq) {
- return decodeHex(separator, csq, 0, GenericUtils.length(csq));
- }
-
- /**
- * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
- * @param csq The {@link CharSequence} containing the HEX encoded bytes
- * @param start Start offset of the HEX sequence (inclusive)
- * @param end End offset of the HEX sequence (exclusive)
- * @return The decoded bytes
- * @throws IllegalArgumentException If invalid HEX sequence length
- * @throws NumberFormatException If invalid HEX characters found
- */
- public static byte[] decodeHex(char separator, CharSequence csq, int start, int end) {
- int len = end - start;
- ValidateUtils.checkTrue(len >= 0, "Bad HEX sequence length: %d", len);
- if (len == 0) {
- return GenericUtils.EMPTY_BYTE_ARRAY;
- }
-
- int delta = 2;
- byte[] bytes;
- if (separator != EMPTY_HEX_SEPARATOR) {
- // last character cannot be the separator
- ValidateUtils.checkTrue((len % 3) == 2, "Invalid separated HEX sequence length: %d", len);
- bytes = new byte[(len + 1) / 3];
- delta++;
- } else {
- ValidateUtils.checkTrue((len & 0x01) == 0, "Invalid contiguous HEX sequence length: %d", len);
- bytes = new byte[len >>> 1];
- }
-
- int writeLen = 0;
- for (int curPos = start; curPos < end; curPos += delta, writeLen++) {
- bytes[writeLen] = fromHex(csq.charAt(curPos), csq.charAt(curPos + 1));
- }
- assert writeLen == bytes.length;
-
- return bytes;
- }
-
- /**
- * @param <S> The {@link OutputStream} generic type
- * @param stream The target {@link OutputStream}
- * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
- * @param csq The {@link CharSequence} containing the HEX encoded bytes
- * @return The number of bytes written to the stream
- * @throws IOException If failed to write
- * @throws IllegalArgumentException If invalid HEX sequence length
- * @throws NumberFormatException If invalid HEX characters found
- * @see #decodeHex(OutputStream, char, CharSequence, int, int)
- */
- public static <S extends OutputStream> int decodeHex(S stream, char separator, CharSequence csq) throws IOException {
- return decodeHex(stream, separator, csq, 0, GenericUtils.length(csq));
- }
-
- /**
- * @param <S> The {@link OutputStream} generic type
- * @param stream The target {@link OutputStream}
- * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
- * @param csq The {@link CharSequence} containing the HEX encoded bytes
- * @param start Start offset of the HEX sequence (inclusive)
- * @param end End offset of the HEX sequence (exclusive)
- * @return The number of bytes written to the stream
- * @throws IOException If failed to write
- * @throws IllegalArgumentException If invalid HEX sequence length
- * @throws NumberFormatException If invalid HEX characters found
- */
- public static <S extends OutputStream> int decodeHex(S stream, char separator, CharSequence csq, int start, int end) throws IOException {
- int len = end - start;
- ValidateUtils.checkTrue(len >= 0, "Bad HEX sequence length: %d", len);
-
- int delta = 2;
- if (separator != EMPTY_HEX_SEPARATOR) {
- // last character cannot be the separator
- ValidateUtils.checkTrue((len % 3) == 2, "Invalid separated HEX sequence length: %d", len);
- delta++;
- } else {
- ValidateUtils.checkTrue((len & 0x01) == 0, "Invalid contiguous HEX sequence length: %d", len);
- }
-
- int writeLen = 0;
- for (int curPos = start; curPos < end; curPos += delta, writeLen++) {
- stream.write(fromHex(csq.charAt(curPos), csq.charAt(curPos + 1)) & 0xFF);
- }
-
- return writeLen;
- }
-
- public static byte fromHex(char hi, char lo) throws NumberFormatException {
- int hiValue = HEX_DIGITS.indexOf(((hi >= 'A') && (hi <= 'F')) ? ('a' + (hi - 'A')) : hi);
- int loValue = HEX_DIGITS.indexOf(((lo >= 'A') && (lo <= 'F')) ? ('a' + (lo - 'A')) : lo);
- if ((hiValue < 0) || (loValue < 0)) {
- throw new NumberFormatException("fromHex(" + new String(new char[]{hi, lo}) + ") non-HEX characters");
- }
-
- return (byte) ((hiValue << 4) + loValue);
- }
-
- /**
- * Read a 32-bit value in network order
- *
- * @param input The {@link InputStream}
- * @param buf Work buffer to use
- * @return The read 32-bit value
- * @throws IOException If failed to read 4 bytes or not enough room in
- * @see #readInt(InputStream, byte[], int, int)
- */
- public static int readInt(InputStream input, byte[] buf) throws IOException {
- return readInt(input, buf, 0, NumberUtils.length(buf));
- }
-
- /**
- * Read a 32-bit value in network order
- *
- * @param input The {@link InputStream}
- * @param buf Work buffer to use
- * @param offset Offset in buffer to us
- * @param len Available length - must have at least 4 bytes available
- * @return The read 32-bit value
- * @throws IOException If failed to read 4 bytes or not enough room in
- * work buffer
- * @see #readUInt(InputStream, byte[], int, int)
- */
- public static int readInt(InputStream input, byte[] buf, int offset, int len) throws IOException {
- return (int) readUInt(input, buf, offset, len);
- }
-
- /**
- * Read a 32-bit value in network order
- *
- * @param input The {@link InputStream}
- * @param buf Work buffer to use
- * @return The read 32-bit value
- * @throws IOException If failed to read 4 bytes or not enough room in
- * @see #readUInt(InputStream, byte[], int, int)
- */
- public static long readUInt(InputStream input, byte[] buf) throws IOException {
- return readUInt(input, buf, 0, NumberUtils.length(buf));
- }
-
- /**
- * Read a 32-bit value in network order
- *
- * @param input The {@link InputStream}
- * @param buf Work buffer to use
- * @param offset Offset in buffer to us
- * @param len Available length - must have at least 4 bytes available
- * @return The read 32-bit value
- * @throws IOException If failed to read 4 bytes or not enough room in
- * work buffer
- * @see #getUInt(byte[], int, int)
- */
- public static long readUInt(InputStream input, byte[] buf, int offset, int len) throws IOException {
- try {
- if (len < Integer.BYTES) {
- throw new IllegalArgumentException("Not enough data for a UINT: required=" + Integer.BYTES + ", available=" + len);
- }
-
- IoUtils.readFully(input, buf, offset, Integer.BYTES);
- return getUInt(buf, offset, len);
- } catch (RuntimeException | Error e) {
- throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
- + " to read UINT value: " + e.getMessage());
- }
- }
-
- /**
- * @param buf A buffer holding a 32-bit unsigned integer in <B>big endian</B>
- * format. <B>Note:</B> if more than 4 bytes are available, then only the
- * <U>first</U> 4 bytes in the buffer will be used
- * @return The result as a {@code long} whose 32 high-order bits are zero
- * @see #getUInt(byte[], int, int)
- */
- public static long getUInt(byte... buf) {
- return getUInt(buf, 0, NumberUtils.length(buf));
- }
-
- /**
- * @param buf A buffer holding a 32-bit unsigned integer in <B>big endian</B>
- * format.
- * @param off The offset of the data in the buffer
- * @param len The available data length. <B>Note:</B> if more than 4 bytes
- * are available, then only the <U>first</U> 4 bytes in the buffer will be
- * used (starting at the specified <tt>offset</tt>)
- * @return The result as a {@code long} whose 32 high-order bits are zero
- */
- public static long getUInt(byte[] buf, int off, int len) {
- if (len < Integer.BYTES) {
- throw new IllegalArgumentException("Not enough data for a UINT: required=" + Integer.BYTES + ", available=" + len);
- }
-
- long l = (buf[off] << 24) & 0xff000000L;
- l |= (buf[off + 1] << 16) & 0x00ff0000L;
- l |= (buf[off + 2] << 8) & 0x0000ff00L;
- l |= (buf[off + 3]) & 0x000000ffL;
- return l;
- }
-
- /**
- * Writes a 32-bit value in network order (i.e., MSB 1st)
- *
- * @param output The {@link OutputStream} to write the value
- * @param value The 32-bit value
- * @param buf A work buffer to use - must have enough space to contain 4 bytes
- * @throws IOException If failed to write the value or work buffer to small
- * @see #writeInt(OutputStream, int, byte[], int, int)
- */
- public static void writeInt(OutputStream output, int value, byte[] buf) throws IOException {
- writeUInt(output, value, buf, 0, NumberUtils.length(buf));
- }
-
- /**
- * Writes a 32-bit value in network order (i.e., MSB 1st)
- *
- * @param output The {@link OutputStream} to write the value
- * @param value The 32-bit value
- * @param buf A work buffer to use - must have enough space to contain 4 bytes
- * @param off The offset to write the value
- * @param len The available space
- * @throws IOException If failed to write the value or work buffer to small
- * @see #writeUInt(OutputStream, long, byte[], int, int)
- */
- public static void writeInt(OutputStream output, int value, byte[] buf, int off, int len) throws IOException {
- writeUInt(output, value & 0xFFFFFFFFL, buf, off, len);
- }
-
- /**
- * Writes a 32-bit value in network order (i.e., MSB 1st)
- *
- * @param output The {@link OutputStream} to write the value
- * @param value The 32-bit value
- * @param buf A work buffer to use - must have enough space to contain 4 bytes
- * @throws IOException If failed to write the value or work buffer to small
- * @see #writeUInt(OutputStream, long, byte[], int, int)
- */
- public static void writeUInt(OutputStream output, long value, byte[] buf) throws IOException {
- writeUInt(output, value, buf, 0, NumberUtils.length(buf));
- }
-
- /**
- * Writes a 32-bit value in network order (i.e., MSB 1st)
- *
- * @param output The {@link OutputStream} to write the value
- * @param value The 32-bit value
- * @param buf A work buffer to use - must have enough space to contain 4 bytes
- * @param off The offset to write the value
- * @param len The available space
- * @throws IOException If failed to write the value or work buffer to small
- * @see #putUInt(long, byte[], int, int)
- */
- public static void writeUInt(OutputStream output, long value, byte[] buf, int off, int len) throws IOException {
- try {
- int writeLen = putUInt(value, buf, off, len);
- output.write(buf, off, writeLen);
- } catch (RuntimeException | Error e) {
- throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
- + " to write UINT value=" + value + ": " + e.getMessage());
- }
- }
-
- /**
- * Writes a 32-bit value in network order (i.e., MSB 1st)
- *
- * @param value The 32-bit value
- * @param buf The buffer
- * @return The number of bytes used in the buffer
- * @throws IllegalArgumentException if not enough space available
- * @see #putUInt(long, byte[], int, int)
- */
- public static int putUInt(long value, byte[] buf) {
- return putUInt(value, buf, 0, NumberUtils.length(buf));
- }
-
- /**
- * Writes a 32-bit value in network order (i.e., MSB 1st)
- *
- * @param value The 32-bit value
- * @param buf The buffer
- * @param off The offset to write the value
- * @param len The available space
- * @return The number of bytes used in the buffer
- * @throws IllegalArgumentException if not enough space available
- */
- public static int putUInt(long value, byte[] buf, int off, int len) {
- if (len < Integer.BYTES) {
- throw new IllegalArgumentException("Not enough data for a UINT: required=" + Integer.BYTES + ", available=" + len);
- }
-
- buf[off] = (byte) ((value >> 24) & 0xFF);
- buf[off + 1] = (byte) ((value >> 16) & 0xFF);
- buf[off + 2] = (byte) ((value >> 8) & 0xFF);
- buf[off + 3] = (byte) (value & 0xFF);
-
- return Integer.BYTES;
- }
-
- public static boolean equals(byte[] a1, byte[] a2) {
- int len1 = NumberUtils.length(a1);
- int len2 = NumberUtils.length(a2);
- if (len1 != len2) {
- return false;
- } else {
- return equals(a1, 0, a2, 0, len1);
- }
- }
-
- @SuppressWarnings("PMD.AssignmentInOperand")
- public static boolean equals(byte[] a1, int a1Offset, byte[] a2, int a2Offset, int length) {
- int len1 = NumberUtils.length(a1);
- int len2 = NumberUtils.length(a2);
- if ((len1 < (a1Offset + length)) || (len2 < (a2Offset + length))) {
- return false;
- }
-
- while (length-- > 0) {
- if (a1[a1Offset++] != a2[a2Offset++]) {
- return false;
- }
- }
-
- return true;
- }
-
- public static int getNextPowerOf2(int value) {
- // for 0-7 return 8
- return (value < Byte.SIZE) ? Byte.SIZE : NumberUtils.getNextPowerOf2(value);
- }
-
- /**
- * Used for encodings where we don't know the data length before adding it
- * to the buffer. The idea is to place a 32-bit "placeholder",
- * encode the data and then return back to the placeholder and update the
- * length. The method calculates the encoded data length, moves the write
- * position to the specified placeholder position, updates the length value
- * and then moves the write position it back to its original value.
- *
- * @param buffer The {@link Buffer}
- * @param lenPos The offset in the buffer where the length placeholder is
- * to be update - <B>Note:</B> assumption is that the encoded data starts
- * <U>immediately</U> after the placeholder
- * @return The amount of data that has been encoded
- */
- public static int updateLengthPlaceholder(Buffer buffer, int lenPos) {
- int startPos = lenPos + Integer.BYTES;
- int endPos = buffer.wpos();
- int dataLength = endPos - startPos;
- // NOTE: although data length is defined as UINT32, we do not expected sizes above Integer.MAX_VALUE
- ValidateUtils.checkTrue(dataLength >= 0, "Illegal data length: %d", dataLength);
- buffer.wpos(lenPos);
- buffer.putInt(dataLength);
- buffer.wpos(endPos);
- return dataLength;
- }
-
- /**
- * Updates a 32-bit "placeholder" location for data length - moves
- * the write position to the specified placeholder position, updates the length
- * value and then moves the write position it back to its original value.
- *
- * @param buffer The {@link Buffer}
- * @param lenPos The offset in the buffer where the length placeholder is
- * to be update - <B>Note:</B> assumption is that the encoded data starts
- * <U>immediately</U> after the placeholder
- * @param dataLength The length to update
- */
- public static void updateLengthPlaceholder(Buffer buffer, int lenPos, int dataLength) {
- int curPos = buffer.wpos();
- buffer.wpos(lenPos);
- buffer.putInt(dataLength);
- buffer.wpos(curPos);
- }
-
- /**
- * Invokes {@link Buffer#clear()}
- *
- * @param <B> The generic buffer type
- * @param buffer A {@link Buffer} instance - ignored if {@code null}
- * @return The same as the input instance
- */
- public static <B extends Buffer> B clear(B buffer) {
- if (buffer != null) {
- buffer.clear();
- }
-
- return buffer;
- }
-
- public static long validateInt32Value(long value, String message) {
- ValidateUtils.checkTrue(isValidInt32Value(value), message, value);
- return value;
- }
-
- public static long validateInt32Value(long value, String format, Object arg) {
- ValidateUtils.checkTrue(isValidInt32Value(value), format, arg);
- return value;
- }
-
- public static long validateInt32Value(long value, String format, Object... args) {
- ValidateUtils.checkTrue(isValidInt32Value(value), format, args);
- return value;
- }
-
- public static boolean isValidInt32Value(long value) {
- return (value >= Integer.MIN_VALUE) && (value <= Integer.MAX_VALUE);
- }
-
- public static long validateUint32Value(long value, String message) {
- ValidateUtils.checkTrue(isValidUint32Value(value), message, value);
- return value;
- }
-
- public static long validateUint32Value(long value, String format, Object arg) {
- ValidateUtils.checkTrue(isValidUint32Value(value), format, arg);
- return value;
- }
-
- public static long validateUint32Value(long value, String format, Object... args) {
- ValidateUtils.checkTrue(isValidUint32Value(value), format, args);
- return value;
- }
-
- public static boolean isValidUint32Value(long value) {
- return (value >= 0L) && (value <= MAX_UINT32_VALUE);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
deleted file mode 100644
index 6655ec1..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
+++ /dev/null
@@ -1,253 +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.util.buffer;
-
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.util.Arrays;
-import java.util.Objects;
-import java.util.function.IntUnaryOperator;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.Readable;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Provides an implementation of {@link Buffer} using a backing byte array
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ByteArrayBuffer extends Buffer {
- public static final int DEFAULT_SIZE = 256;
- public static final int MAX_LEN = 65536;
-
- private byte[] data;
- private int rpos;
- private int wpos;
-
- public ByteArrayBuffer() {
- this(DEFAULT_SIZE);
- }
-
- public ByteArrayBuffer(int size) {
- this(size, true);
- }
-
- public ByteArrayBuffer(int size, boolean roundOff) {
- this(new byte[roundOff ? BufferUtils.getNextPowerOf2(size) : size], false);
- }
-
- public ByteArrayBuffer(byte[] data) {
- this(data, 0, data.length, true);
- }
-
- public ByteArrayBuffer(byte[] data, boolean read) {
- this(data, 0, data.length, read);
- }
-
- public ByteArrayBuffer(byte[] data, int off, int len) {
- this(data, off, len, true);
- }
-
- public ByteArrayBuffer(byte[] data, int off, int len, boolean read) {
- this.data = data;
- this.rpos = off;
- this.wpos = (read ? len : 0) + off;
- }
-
- @Override
- public int rpos() {
- return rpos;
- }
-
- @Override
- public void rpos(int rpos) {
- this.rpos = rpos;
- }
-
- @Override
- public int wpos() {
- return wpos;
- }
-
- @Override
- public void wpos(int wpos) {
- if (wpos > this.wpos) {
- ensureCapacity(wpos - this.wpos);
- }
- this.wpos = wpos;
- }
-
- @Override
- public int available() {
- return wpos - rpos;
- }
-
- @Override
- public int capacity() {
- return data.length - wpos;
- }
-
- @Override
- public byte[] array() {
- return data;
- }
-
- @Override
- public void compact() {
- int avail = available();
- if (avail > 0) {
- System.arraycopy(data, rpos, data, 0, avail);
- }
- wpos -= rpos;
- rpos = 0;
- }
-
- @Override
- public void clear(boolean wipeData) {
- rpos = 0;
- wpos = 0;
-
- if (wipeData) {
- Arrays.fill(data, (byte) 0);
- }
- }
-
- @Override
- public byte getByte() {
- ensureAvailable(Byte.BYTES);
- return data[rpos++];
- }
-
- @Override
- public void putByte(byte b) {
- ensureCapacity(Byte.BYTES);
- data[wpos++] = b;
- }
-
- @Override
- public int putBuffer(Readable buffer, boolean expand) {
- int r = expand ? buffer.available() : Math.min(buffer.available(), capacity());
- ensureCapacity(r);
- buffer.getRawBytes(data, wpos, r);
- wpos += r;
- return r;
- }
-
- @Override
- public void putBuffer(ByteBuffer buffer) {
- int required = buffer.remaining();
- ensureCapacity(required + Integer.SIZE);
- putInt(required);
- buffer.get(data, wpos, required);
- wpos += required;
- }
-
- @Override
- public void putRawBytes(byte[] d, int off, int len) {
- ValidateUtils.checkTrue(len >= 0, "Negative raw bytes length: %d", len);
- ensureCapacity(len);
- System.arraycopy(d, off, data, wpos, len);
- wpos += len;
- }
-
- @Override
- public String getString(Charset charset) {
- int len = getInt();
- if (len < 0) {
- throw new BufferException("Bad item length: " + len);
- }
- ensureAvailable(len);
-
- Objects.requireNonNull(charset, "No charset specified");
- String s = new String(data, rpos, len, charset);
- rpos += len;
- return s;
- }
-
- @Override
- public void getRawBytes(byte[] buf, int off, int len) {
- ensureAvailable(len);
- copyRawBytes(0, buf, off, len);
- rpos += len;
- }
-
- @Override
- protected void copyRawBytes(int offset, byte[] buf, int pos, int len) {
- System.arraycopy(data, rpos + offset, buf, pos, len);
- }
-
- @Override
- public void ensureCapacity(int capacity, IntUnaryOperator growthFactor) {
- ValidateUtils.checkTrue(capacity >= 0, "Negative capacity requested: %d", capacity);
-
- int maxSize = size();
- int curPos = wpos();
- int remaining = maxSize - curPos;
- if (remaining < capacity) {
- int minimum = curPos + capacity;
- int actual = growthFactor.applyAsInt(minimum);
- if (actual < minimum) {
- throw new IllegalStateException("ensureCapacity(" + capacity + ") actual (" + actual + ") below min. (" + minimum + ")");
- }
- byte[] tmp = new byte[actual];
- System.arraycopy(data, 0, tmp, 0, data.length);
- data = tmp;
- }
- }
-
- @Override
- protected int size() {
- return data.length;
- }
-
- /**
- * Creates a compact buffer (i.e., one that starts at offset zero) containing a <U>copy</U>
- * of the original data
- *
- * @param data The original data buffer
- * @return A {@link ByteArrayBuffer} containing a <U>copy</U> of the original data
- * starting at zero read position
- * @see #getCompactClone(byte[], int, int)
- */
- public static ByteArrayBuffer getCompactClone(byte[] data) {
- return getCompactClone(data, 0, NumberUtils.length(data));
- }
-
- /**
- * Creates a compact buffer (i.e., one that starts at offset zero) containing a <U>copy</U>
- * of the original data
- *
- * @param data The original data buffer
- * @param offset The offset of the valid data in the buffer
- * @param len The size (in bytes) of of the valid data in the buffer
- * @return A {@link ByteArrayBuffer} containing a <U>copy</U> of the original data
- * starting at zero read position
- */
- public static ByteArrayBuffer getCompactClone(byte[] data, int offset, int len) {
- byte[] clone = (len > 0) ? new byte[len] : GenericUtils.EMPTY_BYTE_ARRAY;
- if (len > 0) {
- System.arraycopy(data, offset, clone, 0, len);
- }
-
- return new ByteArrayBuffer(clone, true);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/AbstractBufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/AbstractBufferPublicKeyParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/AbstractBufferPublicKeyParser.java
deleted file mode 100644
index aaf9641..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/AbstractBufferPublicKeyParser.java
+++ /dev/null
@@ -1,88 +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.util.buffer.keys;
-
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.KeySpec;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @param <PUB> Type of {@link PublicKey} being extracted
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractBufferPublicKeyParser<PUB extends PublicKey> implements BufferPublicKeyParser<PUB> {
- private final Class<PUB> keyClass;
- private final Collection<String> supported;
-
- protected AbstractBufferPublicKeyParser(Class<PUB> keyClass, String... supported) {
- this(keyClass, GenericUtils.isEmpty(supported) ? Collections.emptyList() : Arrays.asList(supported));
- }
-
- protected AbstractBufferPublicKeyParser(Class<PUB> keyClass, Collection<String> supported) {
- this.keyClass = Objects.requireNonNull(keyClass, "No key class");
- this.supported = ValidateUtils.checkNotNullAndNotEmpty(supported, "No supported types for %s", keyClass.getSimpleName());
- }
-
- public Collection<String> getSupportedKeyTypes() {
- return supported;
- }
-
- public final Class<PUB> getKeyClass() {
- return keyClass;
- }
-
- @Override
- public boolean isKeyTypeSupported(String keyType) {
- Collection<String> keys = getSupportedKeyTypes();
- return (GenericUtils.length(keyType) > 0)
- && (GenericUtils.size(keys) > 0)
- && keys.contains(keyType);
- }
-
- protected <S extends KeySpec> PUB generatePublicKey(String algorithm, S keySpec) throws GeneralSecurityException {
- KeyFactory keyFactory = getKeyFactory(algorithm);
- PublicKey key = keyFactory.generatePublic(keySpec);
- Class<PUB> kc = getKeyClass();
- if (!kc.isInstance(key)) {
- throw new InvalidKeySpecException("Mismatched generated key types: expected=" + kc.getSimpleName() + ", actual=" + key);
- }
-
- return kc.cast(key);
- }
-
- protected KeyFactory getKeyFactory(String algorithm) throws GeneralSecurityException {
- return SecurityUtils.getKeyFactory(algorithm);
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + " - supported=" + getSupportedKeyTypes();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java
deleted file mode 100644
index 6f7cde6..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java
+++ /dev/null
@@ -1,111 +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.util.buffer.keys;
-
-import java.security.GeneralSecurityException;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.util.Arrays;
-import java.util.Collection;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-
-/**
- * Parses a raw {@link PublicKey} from a {@link Buffer}
- *
- * @param <PUB> Type of {@link PublicKey} being extracted
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface BufferPublicKeyParser<PUB extends PublicKey> {
-
- BufferPublicKeyParser<PublicKey> EMPTY = new BufferPublicKeyParser<PublicKey>() {
- @Override
- public boolean isKeyTypeSupported(String keyType) {
- return false;
- }
-
- @Override
- public PublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
- throw new NoSuchAlgorithmException(keyType);
- }
-
- @Override
- public String toString() {
- return "EMPTY";
- }
- };
-
- BufferPublicKeyParser<PublicKey> DEFAULT = aggregate(
- Arrays.asList(
- RSABufferPublicKeyParser.INSTANCE,
- DSSBufferPublicKeyParser.INSTANCE,
- ECBufferPublicKeyParser.INSTANCE,
- ED25519BufferPublicKeyParser.INSTANCE));
-
- /**
- * @param keyType The key type - e.g., "ssh-rsa", "ssh-dss"
- * @return {@code true} if this key type is supported by the parser
- */
- boolean isKeyTypeSupported(String keyType);
-
- /**
- * @param keyType The key type - e.g., "ssh-rsa", "ssh-dss"
- * @param buffer The {@link Buffer} containing the encoded raw public key
- * @return The decoded {@link PublicKey}
- * @throws GeneralSecurityException If failed to generate the key
- */
- PUB getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException;
-
- static BufferPublicKeyParser<PublicKey> aggregate(Collection<? extends BufferPublicKeyParser<? extends PublicKey>> parsers) {
- if (GenericUtils.isEmpty(parsers)) {
- return EMPTY;
- }
-
- return new BufferPublicKeyParser<PublicKey>() {
- @Override
- public boolean isKeyTypeSupported(String keyType) {
- for (BufferPublicKeyParser<? extends PublicKey> p : parsers) {
- if (p.isKeyTypeSupported(keyType)) {
- return true;
- }
- }
-
- return false;
- }
-
- @Override
- public PublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
- for (BufferPublicKeyParser<? extends PublicKey> p : parsers) {
- if (p.isKeyTypeSupported(keyType)) {
- return p.getRawPublicKey(keyType, buffer);
- }
- }
-
- throw new NoSuchAlgorithmException("No aggregate matcher for " + keyType);
- }
-
- @Override
- public String toString() {
- return String.valueOf(parsers);
- }
- };
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/DSSBufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/DSSBufferPublicKeyParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/DSSBufferPublicKeyParser.java
deleted file mode 100644
index 4eec49a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/DSSBufferPublicKeyParser.java
+++ /dev/null
@@ -1,52 +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.util.buffer.keys;
-
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.interfaces.DSAPublicKey;
-import java.security.spec.DSAPublicKeySpec;
-
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DSSBufferPublicKeyParser extends AbstractBufferPublicKeyParser<DSAPublicKey> {
- public static final DSSBufferPublicKeyParser INSTANCE = new DSSBufferPublicKeyParser();
-
- public DSSBufferPublicKeyParser() {
- super(DSAPublicKey.class, KeyPairProvider.SSH_DSS);
- }
-
- @Override
- public DSAPublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
- ValidateUtils.checkTrue(isKeyTypeSupported(keyType), "Unsupported key type: %s", keyType);
- BigInteger p = buffer.getMPInt();
- BigInteger q = buffer.getMPInt();
- BigInteger g = buffer.getMPInt();
- BigInteger y = buffer.getMPInt();
-
- return generatePublicKey(KeyUtils.DSS_ALGORITHM, new DSAPublicKeySpec(y, p, q, g));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/ECBufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/ECBufferPublicKeyParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/ECBufferPublicKeyParser.java
deleted file mode 100644
index df0115a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/ECBufferPublicKeyParser.java
+++ /dev/null
@@ -1,81 +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.util.buffer.keys;
-
-import java.security.GeneralSecurityException;
-import java.security.NoSuchAlgorithmException;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ECBufferPublicKeyParser extends AbstractBufferPublicKeyParser<ECPublicKey> {
- public static final ECBufferPublicKeyParser INSTANCE = new ECBufferPublicKeyParser();
-
- public ECBufferPublicKeyParser() {
- super(ECPublicKey.class, ECCurves.KEY_TYPES);
- }
-
- @Override
- public ECPublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
- ValidateUtils.checkTrue(isKeyTypeSupported(keyType), "Unsupported key type: %s", keyType);
- ECCurves curve = ECCurves.fromKeyType(keyType);
- if (curve == null) {
- throw new NoSuchAlgorithmException("Unsupported raw public algorithm: " + keyType);
- }
-
- String curveName = curve.getName();
- ECParameterSpec params = curve.getParameters();
- return getRawECKey(curveName, params, buffer);
- }
-
- protected ECPublicKey getRawECKey(String expectedCurve, ECParameterSpec spec, Buffer buffer) throws GeneralSecurityException {
- String curveName = buffer.getString();
- if (!expectedCurve.equals(curveName)) {
- throw new InvalidKeySpecException("getRawECKey(" + expectedCurve + ") curve name does not match expected: " + curveName);
- }
-
- if (spec == null) {
- throw new InvalidKeySpecException("getRawECKey(" + expectedCurve + ") missing curve parameters");
- }
-
- byte[] octets = buffer.getBytes();
- ECPoint w;
- try {
- w = ECCurves.octetStringToEcPoint(octets);
- } catch (RuntimeException e) {
- throw new InvalidKeySpecException("getRawECKey(" + expectedCurve + ")"
- + " cannot (" + e.getClass().getSimpleName() + ")"
- + " retrieve W value: " + e.getMessage(),
- e);
- }
-
- return generatePublicKey(KeyUtils.EC_ALGORITHM, new ECPublicKeySpec(w, spec));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/ED25519BufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/ED25519BufferPublicKeyParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/ED25519BufferPublicKeyParser.java
deleted file mode 100644
index 61ce6ab..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/ED25519BufferPublicKeyParser.java
+++ /dev/null
@@ -1,47 +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.util.buffer.keys;
-
-import java.security.GeneralSecurityException;
-import java.security.PublicKey;
-
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * TODO complete this when SSHD-440 is done
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ED25519BufferPublicKeyParser extends AbstractBufferPublicKeyParser<PublicKey> {
- public static final ED25519BufferPublicKeyParser INSTANCE = new ED25519BufferPublicKeyParser();
-
- public ED25519BufferPublicKeyParser() {
- super(PublicKey.class, KeyPairProvider.SSH_ED25519);
- }
-
- @Override
- public PublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
- ValidateUtils.checkTrue(isKeyTypeSupported(keyType), "Unsupported key type: %s", keyType);
- byte[] seed = buffer.getBytes();
- return SecurityUtils.generateEDDSAPublicKey(keyType, seed);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/RSABufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/RSABufferPublicKeyParser.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/RSABufferPublicKeyParser.java
deleted file mode 100644
index 363b07e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/keys/RSABufferPublicKeyParser.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.util.buffer.keys;
-
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.RSAPublicKeySpec;
-
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class RSABufferPublicKeyParser extends AbstractBufferPublicKeyParser<RSAPublicKey> {
- public static final RSABufferPublicKeyParser INSTANCE = new RSABufferPublicKeyParser();
-
- public RSABufferPublicKeyParser() {
- super(RSAPublicKey.class, KeyPairProvider.SSH_RSA);
- }
-
- @Override
- public RSAPublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
- ValidateUtils.checkTrue(isKeyTypeSupported(keyType), "Unsupported key type: %s", keyType);
- BigInteger e = buffer.getMPInt();
- BigInteger n = buffer.getMPInt();
- return generatePublicKey(KeyUtils.RSA_ALGORITHM, new RSAPublicKeySpec(n, e));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java
deleted file mode 100644
index 6413ebb..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java
+++ /dev/null
@@ -1,162 +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.util.closeable;
-
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.DefaultCloseFuture;
-import org.apache.sshd.common.future.SshFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-
-/**
- * Provides some default implementations
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractCloseable extends IoBaseCloseable {
-
- public enum State {
- Opened, Graceful, Immediate, Closed
- }
-
- /**
- * Lock object for this session state
- */
- protected final Object lock = new Object();
-
- /**
- * State of this object
- */
- protected final AtomicReference<AbstractCloseable.State> state = new AtomicReference<>(State.Opened);
-
- /**
- * A future that will be set 'closed' when the object is actually closed
- */
- protected final CloseFuture closeFuture;
-
- protected AbstractCloseable() {
- this("");
- }
-
- protected AbstractCloseable(String discriminator) {
- super(discriminator);
- closeFuture = new DefaultCloseFuture(discriminator, lock);
- }
-
- @Override
- public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
- closeFuture.addListener(listener);
- }
-
- @Override
- public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
- closeFuture.removeListener(listener);
- }
-
- @Override
- public final CloseFuture close(boolean immediately) {
- boolean debugEnabled = log.isDebugEnabled();
- if (immediately) {
- if (state.compareAndSet(State.Opened, State.Immediate)
- || state.compareAndSet(State.Graceful, State.Immediate)) {
- if (debugEnabled) {
- log.debug("close({}) Closing immediately", this);
- }
- preClose();
- doCloseImmediately();
- if (debugEnabled) {
- log.debug("close({})[Immediately] closed", this);
- }
- } else {
- if (debugEnabled) {
- log.debug("close({})[Immediately] state already {}", this, state.get());
- }
- }
- } else {
- if (state.compareAndSet(State.Opened, State.Graceful)) {
- if (debugEnabled) {
- log.debug("close({}) Closing gracefully", this);
- }
- preClose();
- SshFuture<CloseFuture> grace = doCloseGracefully();
- if (grace != null) {
- grace.addListener(future -> {
- if (state.compareAndSet(State.Graceful, State.Immediate)) {
- doCloseImmediately();
- if (debugEnabled) {
- log.debug("close({}][Graceful] - operationComplete() closed", AbstractCloseable.this);
- }
- }
- });
- } else {
- if (state.compareAndSet(State.Graceful, State.Immediate)) {
- doCloseImmediately();
- if (debugEnabled) {
- log.debug("close({})[Graceful] closed", this);
- }
- }
- }
- } else {
- if (debugEnabled) {
- log.debug("close({})[Graceful] state already {}", this, state.get());
- }
- }
- }
- return closeFuture;
- }
-
- @Override
- public final boolean isClosed() {
- return state.get() == State.Closed;
- }
-
- @Override
- public final boolean isClosing() {
- return state.get() != State.Opened;
- }
-
- /**
- * preClose is guaranteed to be called before doCloseGracefully or doCloseImmediately.
- * When preClose() is called, isClosing() == true
- */
- protected void preClose() {
- // nothing
- }
-
- protected CloseFuture doCloseGracefully() {
- return null;
- }
-
- /**
- * <P>doCloseImmediately is called once and only once
- * with state == Immediate</P>
- *
- * <P>Overriding methods should always call the base implementation.
- * It may be called concurrently while preClose() or doCloseGracefully is executing</P>
- */
- protected void doCloseImmediately() {
- closeFuture.setClosed();
- state.set(State.Closed);
- }
-
- protected Builder builder() {
- return new Builder(lock);
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractInnerCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractInnerCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractInnerCloseable.java
deleted file mode 100644
index 6518d23..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/AbstractInnerCloseable.java
+++ /dev/null
@@ -1,48 +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.util.closeable;
-
-import org.apache.sshd.common.Closeable;
-import org.apache.sshd.common.future.CloseFuture;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractInnerCloseable extends AbstractCloseable {
- protected AbstractInnerCloseable() {
- this("");
- }
-
- protected AbstractInnerCloseable(String discriminator) {
- super(discriminator);
- }
-
- protected abstract Closeable getInnerCloseable();
-
- @Override
- protected final CloseFuture doCloseGracefully() {
- return getInnerCloseable().close(false);
- }
-
- @Override
- @SuppressWarnings("synthetic-access")
- protected final void doCloseImmediately() {
- getInnerCloseable().close(true).addListener(future -> AbstractInnerCloseable.super.doCloseImmediately());
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/Builder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/Builder.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/Builder.java
deleted file mode 100644
index 847d49c..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/Builder.java
+++ /dev/null
@@ -1,115 +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.util.closeable;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-import org.apache.sshd.common.Closeable;
-import org.apache.sshd.common.future.SshFuture;
-import org.apache.sshd.common.util.ObjectBuilder;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class Builder implements ObjectBuilder<Closeable> {
- private final Object lock;
- private final List<Closeable> closeables = new ArrayList<>();
-
- public Builder(Object lock) {
- this.lock = Objects.requireNonNull(lock, "No lock");
- }
-
- public Builder run(Object id, Runnable r) {
- return close(new SimpleCloseable(id, lock) {
- @Override
- protected void doClose(boolean immediately) {
- try {
- r.run();
- } finally {
- super.doClose(immediately);
- }
- }
- });
- }
-
- @SuppressWarnings("rawtypes")
- public <T extends SshFuture> Builder when(SshFuture<T> future) {
- if (future != null) {
- when(future.getId(), Collections.singleton(future));
- }
- return this;
- }
-
- @SuppressWarnings("rawtypes")
- @SafeVarargs
- public final <T extends SshFuture> Builder when(SshFuture<T>... futures) {
- return when(getClass().getSimpleName(), Arrays.asList(futures));
- }
-
- @SuppressWarnings("rawtypes")
- public <T extends SshFuture> Builder when(Object id, Iterable<? extends SshFuture<T>> futures) {
- return close(new FuturesCloseable<>(id, lock, futures));
- }
-
- public Builder sequential(Closeable... closeables) {
- for (Closeable closeable : closeables) {
- close(closeable);
- }
- return this;
- }
-
- public Builder sequential(Object id, Iterable<Closeable> closeables) {
- return close(new SequentialCloseable(id, lock, closeables));
- }
-
- public Builder parallel(Closeable... closeables) {
- if (closeables.length == 1) {
- close(closeables[0]);
- } else if (closeables.length > 0) {
- parallel(getClass().getSimpleName(), Arrays.asList(closeables));
- }
- return this;
- }
-
- public Builder parallel(Object id, Iterable<? extends Closeable> closeables) {
- return close(new ParallelCloseable(id, lock, closeables));
- }
-
- public Builder close(Closeable c) {
- if (c != null) {
- closeables.add(c);
- }
- return this;
- }
-
- @Override
- public Closeable build() {
- if (closeables.isEmpty()) {
- return new SimpleCloseable(getClass().getSimpleName(), lock);
- } else if (closeables.size() == 1) {
- return closeables.get(0);
- } else {
- return new SequentialCloseable(getClass().getSimpleName(), lock, closeables);
- }
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/FuturesCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/FuturesCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/FuturesCloseable.java
deleted file mode 100644
index af765b7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/FuturesCloseable.java
+++ /dev/null
@@ -1,76 +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.util.closeable;
-
-import java.util.Collections;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.future.DefaultSshFuture;
-import org.apache.sshd.common.future.SshFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-
-/**
- * @param <T> Type of future
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class FuturesCloseable<T extends SshFuture> extends SimpleCloseable {
-
- private final Iterable<? extends SshFuture<T>> futures;
-
- public FuturesCloseable(Object id, Object lock, Iterable<? extends SshFuture<T>> futures) {
- super(id, lock);
- this.futures = (futures == null) ? Collections.emptyList() : futures;
- }
-
- @Override
- protected void doClose(boolean immediately) {
- if (immediately) {
- for (SshFuture<?> f : futures) {
- if (f instanceof DefaultSshFuture) {
- ((DefaultSshFuture<?>) f).setValue(new SshException("Closed"));
- }
- }
- future.setClosed();
- } else {
- AtomicInteger count = new AtomicInteger(1);
- boolean traceEnabled = log.isTraceEnabled();
- SshFutureListener<T> listener = f -> {
- int pendingCount = count.decrementAndGet();
- if (traceEnabled) {
- log.trace("doClose(" + immediately + ") complete pending: " + pendingCount);
- }
- if (pendingCount == 0) {
- future.setClosed();
- }
- };
-
- for (SshFuture<T> f : futures) {
- if (f != null) {
- int pendingCount = count.incrementAndGet();
- if (traceEnabled) {
- log.trace("doClose(" + immediately + ") future pending: " + pendingCount);
- }
- f.addListener(listener);
- }
- }
- listener.operationComplete(null);
- }
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java
deleted file mode 100644
index f4c6d1a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java
+++ /dev/null
@@ -1,35 +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.util.closeable;
-
-import org.apache.sshd.common.Closeable;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class IoBaseCloseable extends AbstractLoggingBean implements Closeable {
- protected IoBaseCloseable() {
- this("");
- }
-
- protected IoBaseCloseable(String discriminator) {
- super(discriminator);
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/ParallelCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/ParallelCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/ParallelCloseable.java
deleted file mode 100644
index 0900cd7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/ParallelCloseable.java
+++ /dev/null
@@ -1,73 +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.util.closeable;
-
-import java.util.Collections;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.common.Closeable;
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-
-/**
- * Waits for a group of {@link Closeable}s to complete in any order, then
- * signals the completion by setting the "parent" future as closed
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ParallelCloseable extends SimpleCloseable {
- private final Iterable<? extends Closeable> closeables;
-
- public ParallelCloseable(Object id, Object lock, Iterable<? extends Closeable> closeables) {
- super(id, lock);
- this.closeables = (closeables == null) ? Collections.emptyList() : closeables;
- }
-
- @Override
- protected void doClose(boolean immediately) {
- AtomicInteger count = new AtomicInteger(1);
- boolean traceEnabled = log.isTraceEnabled();
- SshFutureListener<CloseFuture> listener = f -> {
- int pendingCount = count.decrementAndGet();
- if (traceEnabled) {
- log.trace("doClose(" + immediately + ") completed pending: " + pendingCount);
- }
- if (pendingCount == 0) {
- future.setClosed();
- }
- };
-
- for (Closeable c : closeables) {
- if (c == null) {
- continue;
- }
-
- int pendingCount = count.incrementAndGet();
- if (traceEnabled) {
- log.trace("doClose(" + immediately + ") pending closeables: " + pendingCount);
- }
- c.close(immediately).addListener(listener);
- }
- /*
- * Trigger the last "decrementAndGet" so that the future is marked as closed
- * when last "operationComplete" is invoked (which could be this call...)
- */
- listener.operationComplete(null);
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java
deleted file mode 100644
index 6af51b8..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java
+++ /dev/null
@@ -1,71 +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.util.closeable;
-
-import java.util.Collections;
-import java.util.Iterator;
-
-import org.apache.sshd.common.Closeable;
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-
-/**
- * Waits for a group of {@link Closeable}s to complete in the given order, then
- * signals the completion by setting the "parent" future as closed
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SequentialCloseable extends SimpleCloseable {
- private final Iterable<? extends Closeable> closeables;
-
- public SequentialCloseable(Object id, Object lock, Iterable<? extends Closeable> closeables) {
- super(id, lock);
- this.closeables = (closeables == null) ? Collections.emptyList() : closeables;
- }
-
- @Override
- protected void doClose(boolean immediately) {
- Iterator<? extends Closeable> iterator = closeables.iterator();
- SshFutureListener<CloseFuture> listener = new SshFutureListener<CloseFuture>() {
- @SuppressWarnings("synthetic-access")
- @Override
- public void operationComplete(CloseFuture previousFuture) {
- boolean traceEnabled = log.isTraceEnabled();
- while (iterator.hasNext()) {
- Closeable c = iterator.next();
- if (c != null) {
- if (traceEnabled) {
- log.trace("doClose(" + immediately + ") closing " + c);
- }
- CloseFuture nextFuture = c.close(immediately);
- nextFuture.addListener(this);
- return;
- }
- }
- if (!iterator.hasNext()) {
- if (log.isDebugEnabled()) {
- log.debug("doClose(" + immediately + ") signal close complete");
- }
- future.setClosed();
- }
- }
- };
- listener.operationComplete(null);
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java
deleted file mode 100644
index e360f13..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java
+++ /dev/null
@@ -1,71 +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.util.closeable;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.DefaultCloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class SimpleCloseable extends IoBaseCloseable {
-
- protected final DefaultCloseFuture future;
- protected final AtomicBoolean closing;
-
- public SimpleCloseable(Object id, Object lock) {
- future = new DefaultCloseFuture(id, lock);
- closing = new AtomicBoolean(false);
- }
-
- @Override
- public boolean isClosed() {
- return future.isClosed();
- }
-
- @Override
- public boolean isClosing() {
- return closing.get();
- }
-
- @Override
- public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
- future.addListener(listener);
- }
-
- @Override
- public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
- future.removeListener(listener);
- }
-
- @Override
- public CloseFuture close(boolean immediately) {
- if (closing.compareAndSet(false, true)) {
- doClose(immediately);
- }
- return future;
- }
-
- protected void doClose(boolean immediately) {
- future.setClosed();
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java b/sshd-core/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java
deleted file mode 100644
index c9eca70..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java
+++ /dev/null
@@ -1,96 +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.util.io;
-
-import java.io.IOException;
-import java.nio.channels.Channel;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * A {@code /dev/null} stream that can be closed - in which case it will throw
- * {@link IOException}s if invoked after being closed
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class CloseableEmptyInputStream extends EmptyInputStream implements Channel {
- private final AtomicBoolean open = new AtomicBoolean(true);
-
- public CloseableEmptyInputStream() {
- super();
- }
-
- @Override
- public boolean isOpen() {
- return open.get();
- }
-
- @Override
- public int available() throws IOException {
- if (isOpen()) {
- return super.available();
- } else {
- throw new IOException("available() stream is closed");
- }
- }
-
- @Override
- public int read() throws IOException {
- if (isOpen()) {
- return super.read();
- } else {
- throw new IOException("read() stream is closed");
- }
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- if (isOpen()) {
- return super.read(b, off, len);
- } else {
- throw new IOException("read([])[" + off + "," + len + "] stream is closed");
- }
- }
-
- @Override
- public long skip(long n) throws IOException {
- if (isOpen()) {
- return super.skip(n);
- } else {
- throw new IOException("skip(" + n + ") stream is closed");
- }
- }
-
- @Override
- public synchronized void reset() throws IOException {
- if (isOpen()) {
- super.reset();
- } else {
- throw new IOException("reset() stream is closed");
- }
- }
-
- @Override
- public void close() throws IOException {
- if (open.getAndSet(false)) {
- //noinspection UnnecessaryReturnStatement
- return; // debug breakpoint
- }
- }
-}
[10/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolverTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolverTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolverTest.java
deleted file mode 100644
index 78994cc..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolverTest.java
+++ /dev/null
@@ -1,139 +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.client.config.hosts;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class ConfigFileHostEntryResolverTest extends BaseTestSupport {
- public ConfigFileHostEntryResolverTest() {
- super();
- }
-
- @Test
- public void testConfigFileReload() throws IOException {
- Path dir = getTempTargetRelativeFile(getClass().getSimpleName());
- AtomicInteger reloadCount = new AtomicInteger();
- ConfigFileHostEntryResolver resolver = new ConfigFileHostEntryResolver(assertHierarchyTargetFolderExists(dir).resolve(getCurrentTestName() + ".config.txt")) {
- @Override
- protected List<HostConfigEntry> reloadHostConfigEntries(Path path, String host, int port, String username)
- throws IOException {
- reloadCount.incrementAndGet();
- return super.reloadHostConfigEntries(path, host, port, username);
- }
- };
- Path path = resolver.getPath();
-
- HostConfigEntry expected = new HostConfigEntry(getCurrentTestName(), getCurrentTestName(), 7365, getCurrentTestName());
- testConfigFileReload("Non-existing", path, reloadCount, null, resolver, expected, null);
- testConfigFileReload("Empty", path, reloadCount, Collections.emptyList(), resolver, expected, null);
- testConfigFileReload("Global", path, reloadCount,
- Collections.singletonList(new HostConfigEntry(HostPatternsHolder.ALL_HOSTS_PATTERN, expected.getHost(), expected.getPort(), expected.getUsername())),
- resolver, expected, expected);
- testConfigFileReload("Wildcard", path, reloadCount,
- Arrays.asList(
- new HostConfigEntry(
- HostPatternsHolder.ALL_HOSTS_PATTERN,
- getClass().getSimpleName(),
- 1234,
- getClass().getSimpleName()),
- new HostConfigEntry(
- expected.getHost() + Character.toString(HostPatternsHolder.WILDCARD_PATTERN),
- expected.getHost(),
- expected.getPort(),
- expected.getUsername())),
- resolver, expected, expected);
- testConfigFileReload("Specific", path, reloadCount,
- Arrays.asList(
- new HostConfigEntry(
- HostPatternsHolder.ALL_HOSTS_PATTERN,
- getClass().getSimpleName(),
- 1234,
- getClass().getSimpleName()),
- new HostConfigEntry(
- getClass().getSimpleName() + Character.toString(HostPatternsHolder.WILDCARD_PATTERN),
- getClass().getSimpleName(),
- 1234,
- getClass().getSimpleName()),
- expected),
- resolver, expected, expected);
- }
-
- private static void testConfigFileReload(
- String phase, Path path, AtomicInteger reloadCount,
- Collection<? extends HostConfigEntry> entries,
- HostConfigEntryResolver resolver,
- HostConfigEntry query,
- HostConfigEntry expected)
- throws IOException {
- if (entries == null) {
- if (Files.exists(path)) {
- Files.delete(path);
- }
- } else {
- HostConfigEntry.writeHostConfigEntries(path, entries, IoUtils.EMPTY_OPEN_OPTIONS);
- }
-
- reloadCount.set(0);
-
- for (int index = 1; index < Byte.SIZE; index++) {
- HostConfigEntry actual =
- resolver.resolveEffectiveHost(query.getHostName(), query.getPort(), query.getUsername());
-
- if (entries == null) {
- assertEquals(phase + "[" + index + "]: mismatched reload count", 0, reloadCount.get());
- } else {
- assertEquals(phase + "[" + index + "]: mismatched reload count", 1, reloadCount.get());
- }
-
- if (expected == null) {
- assertNull(phase + "[" + index + "]: Unexpected success for " + query, actual);
- } else {
- assertNotNull(phase + "[" + index + "]: No result for " + query, actual);
- assertNotSame(phase + "[" + index + "]: No cloned result for " + query, expected, actual);
- assertEquals(phase + "[" + index + "]: Mismatched host for " + query,
- expected.getHostName(), actual.getHostName());
- assertEquals(phase + "[" + index + "]: Mismatched port for " + query,
- expected.getPort(), actual.getPort());
- assertEquals(phase + "[" + index + "]: Mismatched user for " + query,
- expected.getUsername(), actual.getUsername());
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolverTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolverTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolverTest.java
index ab3c591..0679fa3 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolverTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryResolverTest.java
@@ -48,7 +48,7 @@ import org.apache.sshd.common.util.net.SshdSocketAddress;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.auth.password.RejectAllPasswordAuthenticator;
import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.FixMethodOrder;
@@ -132,7 +132,7 @@ public class HostConfigEntryResolverTest extends BaseTestSupport {
@Test
public void testPreloadedIdentities() throws Exception {
- KeyPair identity = Utils.getFirstKeyPair(sshd);
+ KeyPair identity = CommonTestSupportUtils.getFirstKeyPair(sshd);
String user = getCurrentTestName();
// make sure authentication is achieved only via the identity public key
sshd.setPublickeyAuthenticator((username, key, session) -> {
@@ -182,10 +182,9 @@ public class HostConfigEntryResolverTest extends BaseTestSupport {
public void testUseIdentitiesOnly() throws Exception {
Path clientIdFile = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
KeyPairProvider clientIdProvider =
- Utils.createTestHostKeyProvider(clientIdFile.resolve(getCurrentTestName() + ".pem"));
-
- KeyPair specificIdentity = Utils.getFirstKeyPair(sshd);
- KeyPair defaultIdentity = Utils.getFirstKeyPair(clientIdProvider);
+ CommonTestSupportUtils.createTestHostKeyProvider(clientIdFile.resolve(getCurrentTestName() + ".pem"));
+ KeyPair specificIdentity = CommonTestSupportUtils.getFirstKeyPair(sshd);
+ KeyPair defaultIdentity = CommonTestSupportUtils.getFirstKeyPair(clientIdProvider);
ValidateUtils.checkTrue(!KeyUtils.compareKeyPairs(specificIdentity, defaultIdentity),
"client identity not different then entry one");
client.setKeyPairProvider(clientIdProvider);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java
deleted file mode 100644
index c306800..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java
+++ /dev/null
@@ -1,325 +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.client.config.hosts;
-
-import java.io.IOException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.regex.Pattern;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class HostConfigEntryTest extends BaseTestSupport {
- public HostConfigEntryTest() {
- super();
- }
-
- @Test
- public void testNegatingPatternOverridesAll() {
- String testHost = "37.77.34.7";
- String[] elements = GenericUtils.split(testHost, '.');
- StringBuilder sb = new StringBuilder(testHost.length() + Byte.SIZE);
- List<HostPatternValue> patterns = new ArrayList<>(elements.length + 1);
- // all wildcard patterns are not negated - only the actual host
- patterns.add(HostPatternsHolder.toPattern(Character.toString(HostPatternsHolder.NEGATION_CHAR_PATTERN) + testHost));
-
- for (int i = 0; i < elements.length; i++) {
- sb.setLength(0);
-
- for (int j = 0; j < elements.length; j++) {
- if (j > 0) {
- sb.append('.');
- }
- if (i == j) {
- sb.append(HostPatternsHolder.WILDCARD_PATTERN);
- } else {
- sb.append(elements[j]);
- }
- }
-
- patterns.add(HostPatternsHolder.toPattern(sb));
- }
-
- for (int index = 0; index < patterns.size(); index++) {
- assertFalse("Unexpected match for " + patterns, HostPatternsHolder.isHostMatch(testHost, 0, patterns));
- Collections.shuffle(patterns);
- }
- }
-
- @Test
- public void testHostWildcardPatternMatching() {
- String pkgName = getClass().getPackage().getName();
- String[] elements = GenericUtils.split(pkgName, '.');
- StringBuilder sb = new StringBuilder(pkgName.length() + Long.SIZE + 1).append(HostPatternsHolder.WILDCARD_PATTERN);
- for (int index = elements.length - 1; index >= 0; index--) {
- sb.append('.').append(elements[index]);
- }
-
- String value = sb.toString();
- HostPatternValue pp = HostPatternsHolder.toPattern(value);
- Pattern pattern = pp.getPattern();
- String domain = value.substring(1); // chomp the wildcard prefix
- for (String host : new String[] {
- getClass().getSimpleName(),
- getCurrentTestName(),
- getClass().getSimpleName() + "-" + getCurrentTestName(),
- getClass().getSimpleName() + "." + getCurrentTestName(),
- }) {
- sb.setLength(0); // start from scratch
- sb.append(host).append(domain);
-
- testCaseInsensitivePatternMatching(sb.toString(), pattern, true);
- }
- }
-
- @Test
- public void testIPAddressWildcardPatternMatching() {
- StringBuilder sb = new StringBuilder().append("10.0.0.");
- int sbLen = sb.length();
-
- Pattern pattern = HostPatternsHolder.toPattern(sb.append(HostPatternsHolder.WILDCARD_PATTERN)).getPattern();
- for (int v = 0; v <= 255; v++) {
- sb.setLength(sbLen); // start from scratch
- sb.append(v);
-
- String address = sb.toString();
- assertTrue("No match for " + address, HostPatternsHolder.isHostMatch(address, pattern));
- }
- }
-
- @Test
- public void testHostSingleCharPatternMatching() {
- String value = getCurrentTestName();
- StringBuilder sb = new StringBuilder(value);
- for (boolean restoreOriginal : new boolean[] {true, false}) {
- for (int index = 0; index < value.length(); index++) {
- sb.setCharAt(index, HostPatternsHolder.SINGLE_CHAR_PATTERN);
- testCaseInsensitivePatternMatching(value, HostPatternsHolder.toPattern(sb.toString()).getPattern(), true);
- if (restoreOriginal) {
- sb.setCharAt(index, value.charAt(index));
- }
- }
- }
- }
-
- @Test
- public void testIPAddressSingleCharPatternMatching() {
- StringBuilder sb = new StringBuilder().append("10.0.0.");
- int sbLen = sb.length();
-
- for (int v = 0; v <= 255; v++) {
- sb.setLength(sbLen); // start from scratch
- sb.append(v);
-
- String address = sb.toString();
- // replace the added digits with single char pattern
- for (int index = sbLen; index < sb.length(); index++) {
- sb.setCharAt(index, HostPatternsHolder.SINGLE_CHAR_PATTERN);
- }
-
- String pattern = sb.toString();
- HostPatternValue pp = HostPatternsHolder.toPattern(pattern);
- assertTrue("No match for " + address + " on pattern=" + pattern, HostPatternsHolder.isHostMatch(address, 0, Collections.singletonList(pp)));
- }
- }
-
- @Test
- public void testIsValidPatternChar() {
- for (char ch = '\0'; ch <= ' '; ch++) {
- assertFalse("Unexpected valid character (0x" + Integer.toHexString(ch & 0xFF) + ")", HostPatternsHolder.isValidPatternChar(ch));
- }
-
- for (char ch = 'a'; ch <= 'z'; ch++) {
- assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
- }
-
- for (char ch = 'A'; ch <= 'Z'; ch++) {
- assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
- }
-
- for (char ch = '0'; ch <= '9'; ch++) {
- assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
- }
-
- for (char ch : new char[] {'-', '_', '.', HostPatternsHolder.SINGLE_CHAR_PATTERN, HostPatternsHolder.WILDCARD_PATTERN}) {
- assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
- }
-
- for (char ch : new char[] {
- '(', ')', '{', '}', '[', ']', '@',
- '#', '$', '^', '&', '%', '~', '<', '>',
- ',', '/', '\\', '\'', '"', ':', ';'
- }) {
- assertFalse("Unexpected valid character: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch));
- }
-
- for (char ch = 0x7E; ch <= 0xFF; ch++) {
- assertFalse("Unexpected valid character (0x" + Integer.toHexString(ch & 0xFF) + ")", HostPatternsHolder.isValidPatternChar(ch));
- }
- }
-
- @Test
- public void testResolvePort() {
- final int originalPort = Short.MAX_VALUE;
- final int preferredPort = 7365;
- assertEquals("Mismatched entry port preference",
- preferredPort, HostConfigEntry.resolvePort(originalPort, preferredPort));
-
- for (int entryPort : new int[] {-1, 0}) {
- assertEquals("Non-preferred original port for entry port=" + entryPort,
- originalPort, HostConfigEntry.resolvePort(originalPort, entryPort));
- }
- }
-
- @Test
- public void testResolveUsername() {
- final String originalUser = getCurrentTestName();
- final String preferredUser = getClass().getSimpleName();
- assertSame("Mismatched entry user preference",
- preferredUser, HostConfigEntry.resolveUsername(originalUser, preferredUser));
-
- for (String entryUser : new String[] {null, ""}) {
- assertSame("Non-preferred original user for entry user='" + entryUser + "'",
- originalUser, HostConfigEntry.resolveUsername(originalUser, entryUser));
- }
- }
-
- @Test
- public void testReadSimpleHostsConfigEntries() throws IOException {
- validateHostConfigEntries(readHostConfigEntries());
- }
-
- @Test
- public void testReadGlobalHostsConfigEntries() throws IOException {
- List<HostConfigEntry> entries = validateHostConfigEntries(readHostConfigEntries());
- assertTrue("Not enough entries read", GenericUtils.size(entries) > 1);
-
- // global entry MUST be 1st one
- HostConfigEntry globalEntry = entries.get(0);
- assertEquals("Mismatched global entry pattern", HostPatternsHolder.ALL_HOSTS_PATTERN, globalEntry.getHost());
-
- for (int index = 1; index < entries.size(); index++) {
- HostConfigEntry entry = entries.get(index);
- assertFalse("No target host for " + entry, GenericUtils.isEmpty(entry.getHostName()));
- assertTrue("No target port for " + entry, entry.getPort() > 0);
- assertFalse("No username for " + entry, GenericUtils.isEmpty(entry.getUsername()));
- assertFalse("No identities for " + entry, GenericUtils.isEmpty(entry.getIdentities()));
- assertFalse("No properties for " + entry, GenericUtils.isEmpty(entry.getProperties()));
- }
- }
-
- @Test
- public void testReadMultipleHostPatterns() throws IOException {
- List<HostConfigEntry> entries = validateHostConfigEntries(readHostConfigEntries());
- assertEquals("Mismatched number of entries", 1, GenericUtils.size(entries));
- assertEquals("Mismatched number of patterns", 3, GenericUtils.size(entries.get(0).getPatterns()));
- }
-
- @Test
- public void testResolveIdentityFilePath() throws Exception {
- final String hostValue = getClass().getSimpleName();
- final int portValue = 7365;
- final String userValue = getCurrentTestName();
-
- Exception err = null;
- for (String pattern : new String[] {
- "~/.ssh/%h.key",
- "%d/.ssh/%h.key",
- "/home/%u/.ssh/id_rsa_%p",
- "/home/%u/.ssh/id_%r_rsa",
- "/home/%u/.ssh/%h/%l.key"
- }) {
- try {
- String result = HostConfigEntry.resolveIdentityFilePath(pattern, hostValue, portValue, userValue);
- System.out.append('\t').append(pattern).append(" => ").println(result);
- } catch (Exception e) {
- System.err.append("Failed (").append(e.getClass().getSimpleName())
- .append(") to process pattern=").append(pattern)
- .append(": ").println(e.getMessage());
- err = e;
- }
- }
-
- if (err != null) {
- throw err;
- }
- }
-
- @Test
- public void testFindBestMatch() {
- final String hostValue = getCurrentTestName();
- HostConfigEntry expected = new HostConfigEntry(hostValue, hostValue, 7365, hostValue);
- List<HostConfigEntry> matches = new ArrayList<>();
- matches.add(new HostConfigEntry(HostPatternsHolder.ALL_HOSTS_PATTERN,
- getClass().getSimpleName(), Short.MAX_VALUE, getClass().getSimpleName()));
- matches.add(new HostConfigEntry(hostValue + Character.toString(HostPatternsHolder.WILDCARD_PATTERN),
- getClass().getSimpleName(), Byte.MAX_VALUE, getClass().getSimpleName()));
- matches.add(expected);
-
- for (int index = 0; index < matches.size(); index++) {
- HostConfigEntry actual = HostConfigEntry.findBestMatch(matches);
- assertSame("Mismatched best match for " + matches, expected, actual);
- Collections.shuffle(matches);
- }
- }
-
- private static <C extends Collection<HostConfigEntry>> C validateHostConfigEntries(C entries) {
- assertFalse("No entries", GenericUtils.isEmpty(entries));
-
- for (HostConfigEntry entry : entries) {
- assertFalse("No pattern for " + entry, GenericUtils.isEmpty(entry.getHost()));
- assertFalse("No extra properties for " + entry, GenericUtils.isEmpty(entry.getProperties()));
- }
-
- return entries;
- }
-
- private List<HostConfigEntry> readHostConfigEntries() throws IOException {
- return readHostConfigEntries(getCurrentTestName() + ".config.txt");
- }
-
- private List<HostConfigEntry> readHostConfigEntries(String resourceName) throws IOException {
- URL url = getClass().getResource(resourceName);
- assertNotNull("Missing resource " + resourceName, url);
- return HostConfigEntry.readHostConfigEntries(url);
- }
-
- private static void testCaseInsensitivePatternMatching(String value, Pattern pattern, boolean expected) {
- for (int index = 0; index < value.length(); index++) {
- boolean actual = HostPatternsHolder.isHostMatch(value, pattern);
- assertEquals("Mismatched match result for " + value + " on pattern=" + pattern.pattern(), expected, actual);
- value = shuffleCase(value);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
deleted file mode 100644
index efbf885..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
+++ /dev/null
@@ -1,79 +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.client.config.hosts;
-
-import java.util.Arrays;
-import java.util.Collection;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class KnownHostHashValueTest extends BaseTestSupport {
- private final String hostName;
- private final String hashValue;
- private final KnownHostHashValue hash;
-
- public KnownHostHashValueTest(String hostName, String hashValue) {
- this.hostName = hostName;
- this.hashValue = hashValue;
- this.hash = KnownHostHashValue.parse(hashValue);
- }
-
- @Parameters(name = "host={0}, hash={1}")
- public static Collection<Object[]> parameters() {
- return Arrays.<Object[]>asList(
- (Object[]) new String[]{"192.168.1.61", "|1|F1E1KeoE/eEWhi10WpGv4OdiO6Y=|3988QV0VE8wmZL7suNrYQLITLCg="});
- }
-
- @Test
- public void testDecodeEncode() {
- assertSame("Mismatched digester", KnownHostHashValue.DEFAULT_DIGEST, hash.getDigester());
- assertEquals("Mismatched encoded form", hashValue, hash.toString());
- }
-
- @Test
- public void testHostMatch() {
- assertTrue("Specified host does not match", hash.isHostMatch(hostName));
- assertFalse("Unexpected host match", hash.isHostMatch(getCurrentTestName()));
- }
-
- @Test
- public void testCalculateHashValue() throws Exception {
- byte[] expected = hash.getDigestValue();
- byte[] actual = KnownHostHashValue.calculateHashValue(hostName, hash.getDigester(), hash.getSaltValue());
- assertArrayEquals("Mismatched hash value", expected, actual);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
deleted file mode 100644
index af720b8..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java
+++ /dev/null
@@ -1,161 +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.client.config.keys;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.EnumMap;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.BuiltinIdentities;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-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.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.apache.sshd.util.test.Utils;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class BuiltinClientIdentitiesWatcherTest extends BaseTestSupport {
- public BuiltinClientIdentitiesWatcherTest() {
- super();
- }
-
- @Test
- public void testMultipleFilesWatch() throws Exception {
- KeyPair identity = Utils.getFirstKeyPair(createTestHostKeyProvider());
- String keyType = ValidateUtils.checkNotNullAndNotEmpty(KeyUtils.getKeyType(identity), "Cannot determine identity key type");
-
- Path dir = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
- Map<BuiltinIdentities, Path> locationsMap = new EnumMap<>(BuiltinIdentities.class);
- Map<BuiltinIdentities, KeyPair> idsMap = new EnumMap<>(BuiltinIdentities.class);
- for (BuiltinIdentities id : BuiltinIdentities.VALUES) {
- Path idFile = dir.resolve(ClientIdentity.getIdentityFileName(id));
- Files.deleteIfExists(idFile);
- assertNull("Multiple file mappings for " + id, locationsMap.put(id, idFile));
- assertNull("Multiple identity mappings for " + id, idsMap.put(id, KeyUtils.cloneKeyPair(keyType, identity)));
- }
-
- ClientIdentityLoader loader = new ClientIdentityLoader() {
- @Override
- public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
- BuiltinIdentities id = findIdentity(location);
- assertNotNull("Invalid location: " + location, id);
- return idsMap.get(id);
- }
-
- @Override
- public boolean isValidLocation(String location) throws IOException {
- return findIdentity(location) != null;
- }
-
- private BuiltinIdentities findIdentity(String location) {
- if (GenericUtils.isEmpty(location)) {
- return null;
- }
-
- for (Map.Entry<BuiltinIdentities, Path> le : locationsMap.entrySet()) {
- Path path = le.getValue();
- if (String.CASE_INSENSITIVE_ORDER.compare(location, path.toString()) == 0) {
- return le.getKey();
- }
- }
-
- return null;
- }
- };
-
- Map<BuiltinIdentities, KeyPair> existing = new EnumMap<>(BuiltinIdentities.class);
- KeyPairProvider watcher = new BuiltinClientIdentitiesWatcher(dir, false, loader, FilePasswordProvider.EMPTY, false);
- testMultipleFilesWatch("No files", watcher, existing.values());
-
- for (BuiltinIdentities id : BuiltinIdentities.VALUES) {
- String phase = id + " + " + Objects.toString(existing.keySet());
- touchIdentityFile(locationsMap.get(id));
- existing.put(id, idsMap.get(id));
-
- for (int index = 0; index < Byte.SIZE; index++) {
- testMultipleFilesWatch(phase + "[" + index + "]", watcher, existing.values());
- }
- }
-
- testMultipleFilesWatch("All files", watcher, existing.values());
-
- for (BuiltinIdentities id : BuiltinIdentities.VALUES) {
- existing.remove(id);
- Files.deleteIfExists(locationsMap.get(id));
- String phase = Objects.toString(existing.keySet()) + " - " + id;
-
- for (int index = 0; index < Byte.SIZE; index++) {
- testMultipleFilesWatch(phase + "[" + index + "]", watcher, existing.values());
- }
- }
- }
-
- private static void touchIdentityFile(Path idFile) throws IOException {
- OpenOption[] options = IoUtils.EMPTY_OPEN_OPTIONS;
- if (Files.exists(idFile, IoUtils.EMPTY_LINK_OPTIONS)) {
- options = new OpenOption[]{StandardOpenOption.WRITE, StandardOpenOption.APPEND};
- }
-
- try (OutputStream out = Files.newOutputStream(idFile, options)) {
- out.write(new Date(System.currentTimeMillis()).toString().getBytes(StandardCharsets.UTF_8));
- out.write('\n');
- }
- }
-
- private static void testMultipleFilesWatch(String phase, KeyIdentityProvider watcher, Collection<? extends KeyPair> expected) {
- Iterable<KeyPair> keys = watcher.loadKeys();
- Collection<KeyPair> actual = new ArrayList<>();
- for (KeyPair kp : keys) {
- actual.add(kp);
- }
- assertEquals(phase + ": mismatched sizes", GenericUtils.size(expected), GenericUtils.size(actual));
-
- if (!GenericUtils.isEmpty(expected)) {
- for (KeyPair kp : expected) {
- assertTrue(phase + ": missing key", actual.contains(kp));
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
deleted file mode 100644
index 60ded0a..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java
+++ /dev/null
@@ -1,121 +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.client.config.keys;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.Date;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.apache.sshd.util.test.Utils;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class ClientIdentityFileWatcherTest extends BaseTestSupport {
- public ClientIdentityFileWatcherTest() {
- super();
- }
-
- @Test
- public void testIdentityReload() throws Exception {
- Path dir = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName()));
- Path idFile = dir.resolve(getCurrentTestName() + ".pem");
- KeyPair identity = Utils.getFirstKeyPair(createTestHostKeyProvider());
- ClientIdentityLoader loader = new ClientIdentityLoader() {
- @Override
- public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
- assertTrue("Invalid location: " + location, isValidLocation(location));
- return identity;
- }
-
- @Override
- public boolean isValidLocation(String location) throws IOException {
- return Objects.equals(location, toString());
- }
-
- @Override
- public String toString() {
- return Objects.toString(idFile);
- }
- };
-
- AtomicInteger reloadCount = new AtomicInteger(0);
- ClientIdentityProvider idProvider = new ClientIdentityFileWatcher(idFile, loader, FilePasswordProvider.EMPTY, false) {
- @Override
- protected KeyPair reloadClientIdentity(Path path) throws IOException, GeneralSecurityException {
- assertEquals("Mismatched client identity path", idFile, path);
- reloadCount.incrementAndGet();
- return super.reloadClientIdentity(path);
- }
- };
- Files.deleteIfExists(idFile);
-
- testIdentityReload("Non-existing", reloadCount, idProvider, null, 0);
-
- touchIdentityFile(idFile);
- for (int index = 1; index < Byte.SIZE; index++) {
- testIdentityReload("Created iteration " + 1, reloadCount, idProvider, identity, 1);
- }
-
- touchIdentityFile(idFile);
- for (int index = 1; index < Byte.SIZE; index++) {
- testIdentityReload("Modified iteration " + 1, reloadCount, idProvider, identity, 2);
- }
- }
-
- private static void touchIdentityFile(Path idFile) throws IOException {
- OpenOption[] options = IoUtils.EMPTY_OPEN_OPTIONS;
- if (Files.exists(idFile, IoUtils.EMPTY_LINK_OPTIONS)) {
- options = new OpenOption[]{StandardOpenOption.WRITE, StandardOpenOption.APPEND};
- }
-
- try (OutputStream out = Files.newOutputStream(idFile, options)) {
- out.write(new Date(System.currentTimeMillis()).toString().getBytes(StandardCharsets.UTF_8));
- out.write('\n');
- }
- }
-
- private static void testIdentityReload(
- String phase, Number reloadCount, ClientIdentityProvider provider, KeyPair expectedIdentity, int expectedCount)
- throws Exception {
- KeyPair actualIdentity = provider.getClientIdentity();
- assertSame(phase + ": mismatched identity", expectedIdentity, actualIdentity);
- assertEquals(phase + ": mismatched re-load count", expectedCount, reloadCount.intValue());
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java b/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
deleted file mode 100644
index 6861299..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
+++ /dev/null
@@ -1,100 +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.client.config.keys;
-
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.security.KeyPair;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.Map;
-
-import org.apache.sshd.common.config.keys.BuiltinIdentities;
-import org.apache.sshd.common.config.keys.IdentityUtils;
-import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class ClientIdentityTest extends BaseTestSupport {
- public ClientIdentityTest() {
- super();
- }
-
- @Test
- public void testLoadClientIdentities() throws Exception {
- Path resFolder = getTestResourcesFolder();
- LinkOption[] options = IoUtils.getLinkOptions(true);
- Collection<BuiltinIdentities> expected = EnumSet.noneOf(BuiltinIdentities.class);
- for (BuiltinIdentities type : BuiltinIdentities.VALUES) {
- String fileName = ClientIdentity.getIdentityFileName(type);
- Path file = resFolder.resolve(fileName);
- if (!Files.exists(file, options)) {
- System.out.println("Skip non-existing identity file " + file);
- continue;
- }
-
- if (!type.isSupported()) {
- System.out.println("Skip unsupported identity file " + file);
- continue;
- }
-
- expected.add(type);
- }
-
- Map<String, KeyPair> ids = ClientIdentity.loadDefaultIdentities(
- resFolder,
- false, // don't be strict
- null, // none of the files is password protected
- options);
- assertEquals("Mismatched loaded ids count", GenericUtils.size(expected), GenericUtils.size(ids));
-
- Collection<KeyPair> pairs = new ArrayList<>(ids.size());
- for (BuiltinIdentities type : BuiltinIdentities.VALUES) {
- if (expected.contains(type)) {
- KeyPair kp = ids.get(type.getName());
- assertNotNull("No key pair loaded for " + type, kp);
- pairs.add(kp);
- }
- }
-
- KeyIdentityProvider provider = IdentityUtils.createKeyPairProvider(ids, true /* supported only */);
- assertNotNull("No provider generated", provider);
-
- Iterable<KeyPair> keys = provider.loadKeys();
- for (KeyPair kp : keys) {
- assertTrue("Unexpected loaded key: " + kp, pairs.remove(kp));
- }
-
- assertEquals("Not all pairs listed", 0, pairs.size());
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java b/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
index 6ad17b2..678521f 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
@@ -41,9 +41,9 @@ import org.apache.sshd.common.kex.KeyExchange;
import org.apache.sshd.common.util.security.SecurityUtils;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
import org.apache.sshd.util.test.TeeOutputStream;
-import org.apache.sshd.util.test.Utils;
import org.junit.AfterClass;
import org.junit.Assume;
import org.junit.BeforeClass;
@@ -81,11 +81,11 @@ public class KexTest extends BaseTestSupport {
@BeforeClass
public static void setupClientAndServer() throws Exception {
- sshd = Utils.setupTestServer(KexTest.class);
+ sshd = CoreTestSupportUtils.setupTestServer(KexTest.class);
sshd.start();
port = sshd.getPort();
- client = Utils.setupTestClient(KexTest.class);
+ client = CoreTestSupportUtils.setupTestClient(KexTest.class);
client.start();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifierTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifierTest.java b/sshd-core/src/test/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifierTest.java
index c556553..9151af2 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifierTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifierTest.java
@@ -51,8 +51,8 @@ import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.net.SshdSocketAddress;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.apache.sshd.util.test.NoIoTestCase;
-import org.apache.sshd.util.test.Utils;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -230,7 +230,7 @@ public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
@Test
public void testRejectModifiedServerKey() throws Exception {
- KeyPair kp = Utils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
+ KeyPair kp = CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
final PublicKey modifiedKey = kp.getPublic();
final AtomicInteger acceptCount = new AtomicInteger(0);
ServerKeyVerifier verifier = new KnownHostsServerKeyVerifier(AcceptAllServerKeyVerifier.INSTANCE, createKnownHostsCopy()) {
@@ -258,7 +258,7 @@ public class KnownHostsServerKeyVerifierTest extends BaseTestSupport {
@Test
public void testAcceptModifiedServerKeyUpdatesFile() throws Exception {
- KeyPair kp = Utils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
+ KeyPair kp = CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
final PublicKey modifiedKey = kp.getPublic();
Path path = createKnownHostsCopy();
ServerKeyVerifier verifier = new KnownHostsServerKeyVerifier(AcceptAllServerKeyVerifier.INSTANCE, path) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java b/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java
index 87dc5ff..12c416d 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java
@@ -29,7 +29,7 @@ import org.apache.sshd.client.SshClient;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.util.test.BaseTestSupport;
import org.apache.sshd.util.test.CommandExecutionHelper;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
@@ -51,11 +51,11 @@ public class ClientSessionTest extends BaseTestSupport {
@BeforeClass
public static void setupClientAndServer() throws Exception {
- sshd = Utils.setupTestServer(ClientSessionTest.class);
+ sshd = CoreTestSupportUtils.setupTestServer(ClientSessionTest.class);
sshd.start();
port = sshd.getPort();
- client = Utils.setupTestClient(ClientSessionTest.class);
+ client = CoreTestSupportUtils.setupTestClient(ClientSessionTest.class);
client.start();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSessionClientTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSessionClientTest.java b/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSessionClientTest.java
index ba3ee68..a095f81 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSessionClientTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/simple/SimpleSessionClientTest.java
@@ -32,7 +32,7 @@ import org.apache.sshd.common.session.SessionListener;
import org.apache.sshd.server.auth.password.PasswordAuthenticator;
import org.apache.sshd.server.auth.password.RejectAllPasswordAuthenticator;
import org.apache.sshd.server.auth.pubkey.RejectAllPublickeyAuthenticator;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.apache.sshd.util.test.client.simple.BaseSimpleClientTestSupport;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -60,8 +60,8 @@ public class SimpleSessionClientTest extends BaseSimpleClientTestSupport {
@Test
public void testLoginSessionWithIdentity() throws Exception {
- final KeyPair identity = Utils.getFirstKeyPair(createTestHostKeyProvider());
- final AtomicBoolean identityQueried = new AtomicBoolean(false);
+ KeyPair identity = CommonTestSupportUtils.getFirstKeyPair(createTestHostKeyProvider());
+ AtomicBoolean identityQueried = new AtomicBoolean(false);
sshd.setPublickeyAuthenticator((username, key, session) -> {
if (username.equals(getCurrentTestName())) {
identityQueried.set(true);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/AttributeStoreTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/AttributeStoreTest.java b/sshd-core/src/test/java/org/apache/sshd/common/AttributeStoreTest.java
index c0f1a73..a8137f0 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/AttributeStoreTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/AttributeStoreTest.java
@@ -44,38 +44,38 @@ public class AttributeStoreTest extends BaseTestSupport {
@Test
public void testResolveFactoryManagerAttribute() {
- assertNull("Unexpected null factory value", AttributeStore.resolveAttribute((FactoryManager) null, KEY));
+ assertNull("Unexpected null factory value", FactoryManager.resolveAttribute((FactoryManager) null, KEY));
FactoryManager manager = Mockito.mock(FactoryManager.class);
String expected = setAttributeValue(manager, getCurrentTestName());
- assertSame("Mismatched resolved value", expected, AttributeStore.resolveAttribute(manager, KEY));
+ assertSame("Mismatched resolved value", expected, FactoryManager.resolveAttribute(manager, KEY));
}
@Test
public void testResolveSessionAttribute() {
- assertNull("Unexpected null session value", AttributeStore.resolveAttribute((Session) null, KEY));
+ assertNull("Unexpected null session value", Session.resolveAttribute((Session) null, KEY));
Session session = Mockito.mock(Session.class);
- final AtomicInteger managerCount = new AtomicInteger(0);
+ AtomicInteger managerCount = new AtomicInteger(0);
Mockito.when(session.getFactoryManager()).then(invocation -> {
managerCount.incrementAndGet();
return null;
});
setAttributeValue(session, null);
- assertNull("Unexpected success for empty attribute", AttributeStore.resolveAttribute(session, KEY));
+ assertNull("Unexpected success for empty attribute", Session.resolveAttribute(session, KEY));
assertEquals("Factory manager not requested", 1, managerCount.getAndSet(0));
String expected = setAttributeValue(session, getCurrentTestName());
- assertSame("Mismatched attribute value", expected, AttributeStore.resolveAttribute(session, KEY));
+ assertSame("Mismatched attribute value", expected, Session.resolveAttribute(session, KEY));
assertEquals("Unexpected manager request", 0, managerCount.get());
}
@Test
public void testResolveChannelAttribute() {
- assertNull("Unexpected null channek value", AttributeStore.resolveAttribute((Channel) null, KEY));
+ assertNull("Unexpected null channek value", Channel.resolveAttribute((Channel) null, KEY));
- final Session session = Mockito.mock(Session.class);
- final AtomicInteger managerCount = new AtomicInteger(0);
+ Session session = Mockito.mock(Session.class);
+ AtomicInteger managerCount = new AtomicInteger(0);
Mockito.when(session.getFactoryManager()).thenAnswer(invocation -> {
managerCount.incrementAndGet();
return null;
@@ -83,19 +83,19 @@ public class AttributeStoreTest extends BaseTestSupport {
setAttributeValue(session, null);
Channel channel = Mockito.mock(Channel.class);
- final AtomicInteger sessionCount = new AtomicInteger(0);
+ AtomicInteger sessionCount = new AtomicInteger(0);
Mockito.when(channel.getSession()).thenAnswer(invocation -> {
sessionCount.incrementAndGet();
return session;
});
setAttributeValue(channel, null);
- assertNull("Unexpected success for empty attribute", AttributeStore.resolveAttribute(channel, KEY));
+ assertNull("Unexpected success for empty attribute", Channel.resolveAttribute(channel, KEY));
assertEquals("Session not requested", 1, sessionCount.getAndSet(0));
assertEquals("Factory manager not requested", 1, managerCount.getAndSet(0));
String expected = setAttributeValue(channel, getCurrentTestName());
- assertSame("Mismatched attribute value", expected, AttributeStore.resolveAttribute(channel, KEY));
+ assertSame("Mismatched attribute value", expected, Channel.resolveAttribute(channel, KEY));
assertEquals("Unexpected session request", 0, sessionCount.get());
assertEquals("Unexpected manager request", 0, managerCount.get());
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/PropertyResolverUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/PropertyResolverUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/PropertyResolverUtilsTest.java
index 12319c0..69b6fe8 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/PropertyResolverUtilsTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/PropertyResolverUtilsTest.java
@@ -30,7 +30,7 @@ import java.util.concurrent.TimeUnit;
import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.JUnitTestSupport;
import org.apache.sshd.util.test.NoIoTestCase;
import org.junit.FixMethodOrder;
import org.junit.Test;
@@ -43,21 +43,21 @@ import org.mockito.Mockito;
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Category({ NoIoTestCase.class })
-public class PropertyResolverUtilsTest extends BaseTestSupport {
+public class PropertyResolverUtilsTest extends JUnitTestSupport {
public PropertyResolverUtilsTest() {
super();
}
@Test
public void testResolveAndUpdateClosestPropertyValue() {
- final String propName = getCurrentTestName();
- final String rootValue = getClass().getPackage().getName();
+ String propName = getCurrentTestName();
+ String rootValue = getClass().getPackage().getName();
Session resolver = createMockSession();
FactoryManager root = Objects.requireNonNull(resolver.getFactoryManager(), "No manager");
assertNull("Unexpected root previous value", PropertyResolverUtils.updateProperty(root, propName, rootValue));
assertSame("Mismatched root value", rootValue, PropertyResolverUtils.getString(resolver, propName));
- final String nodeValue = getClass().getSimpleName();
+ String nodeValue = getClass().getSimpleName();
assertNull("Unexpected node previous value", PropertyResolverUtils.updateProperty(resolver, propName, nodeValue));
assertSame("Mismatched node value", nodeValue, PropertyResolverUtils.getString(resolver, propName));
}
@@ -90,7 +90,7 @@ public class PropertyResolverUtilsTest extends BaseTestSupport {
System.clearProperty(propKey);
}
- for (final boolean expected : new boolean[]{false, true}) {
+ for (boolean expected : new boolean[]{false, true}) {
try {
System.setProperty(propKey, Boolean.toString(expected));
testBooleanProperty(resolver, propName, expected);
@@ -102,8 +102,8 @@ public class PropertyResolverUtilsTest extends BaseTestSupport {
@Test
public void testLongProperty() {
- final long expected = System.currentTimeMillis();
- final String name = getCurrentTestName();
+ long expected = System.currentTimeMillis();
+ String name = getCurrentTestName();
Session session = createMockSession();
assertEquals("Mismatched empty props value", expected, PropertyResolverUtils.getLongProperty(session, name, expected));
@@ -137,8 +137,8 @@ public class PropertyResolverUtilsTest extends BaseTestSupport {
@Test
public void testIntegerProperty() {
- final int expected = 3777347;
- final String name = getCurrentTestName();
+ int expected = 3777347;
+ String name = getCurrentTestName();
Session session = createMockSession();
assertEquals("Mismatched empty props value", expected, PropertyResolverUtils.getIntProperty(session, name, expected));
@@ -176,8 +176,8 @@ public class PropertyResolverUtilsTest extends BaseTestSupport {
@Test
public void testBooleanProperty() {
- for (final boolean expected : new boolean[]{false, true}) {
- final String name = getCurrentTestName();
+ for (boolean expected : new boolean[]{false, true}) {
+ String name = getCurrentTestName();
Session session = createMockSession();
assertEquals("Mismatched empty props value", expected, PropertyResolverUtils.getBooleanProperty(session, name, expected));
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/SshConstantsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/SshConstantsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/SshConstantsTest.java
deleted file mode 100644
index a15ca6b..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/SshConstantsTest.java
+++ /dev/null
@@ -1,75 +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;
-
-import java.util.Collection;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class SshConstantsTest extends BaseTestSupport {
- public SshConstantsTest() {
- super();
- }
-
- @Test
- public void testGetDisconnectReason() {
- for (int reason = SshConstants.SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT; reason <= SshConstants.SSH2_DISCONNECT_ILLEGAL_USER_NAME; reason++) {
- String name = SshConstants.getDisconnectReasonName(reason);
- assertTrue("Mismatched name for reason=" + reason + ": " + name, name.startsWith("SSH2_DISCONNECT_"));
- }
- }
-
- @Test
- public void testGetOpenErrorName() {
- for (int code = SshConstants.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; code <= SshConstants.SSH_OPEN_RESOURCE_SHORTAGE; code++) {
- String name = SshConstants.getOpenErrorCodeName(code);
- assertTrue("Mismatched name for code=" + code + ": " + name, name.startsWith("SSH_OPEN_"));
- }
- }
-
- @Test
- public void testAmbiguousOpcodes() throws Exception {
- int[] knownAmbiguities = {30, 31, 60};
- Collection<Integer> opcodes = SshConstants.getAmbiguousOpcodes();
- assertTrue("Not enough ambiguities found", GenericUtils.size(opcodes) >= knownAmbiguities.length);
-
- for (int cmd : knownAmbiguities) {
- assertEquals("Mismatched mnemonic for known ambiguity=" + cmd, Integer.toString(cmd), SshConstants.getCommandMessageName(cmd));
- assertTrue("Known ambiguity not reported as such: " + cmd, SshConstants.isAmbiguousOpcode(cmd));
- assertTrue("Known ambiguity=" + cmd + " not listed: " + opcodes, opcodes.contains(cmd));
- }
-
- for (Integer cmd : opcodes) {
- assertEquals("Mismatched mnemonic for " + cmd, cmd.toString(), SshConstants.getCommandMessageName(cmd));
- assertTrue("Opcode not detected as ambiguous: " + cmd, SshConstants.isAmbiguousOpcode(cmd));
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java b/sshd-core/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java
deleted file mode 100644
index 06cfffc..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/VersionPropertiesTest.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;
-
-import java.util.Map;
-
-import org.apache.sshd.common.config.VersionProperties;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class VersionPropertiesTest extends BaseTestSupport {
- public VersionPropertiesTest() {
- super();
- }
-
- @Test
- public void testNonEmptyProperties() {
- Map<?, ?> props = VersionProperties.getVersionProperties();
- assertTrue(GenericUtils.isNotEmpty(props));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java b/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
index 322f55c..d7df493 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
@@ -74,7 +74,7 @@ import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.session.ServerSessionImpl;
import org.apache.sshd.server.session.SessionFactory;
import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.FixMethodOrder;
@@ -587,14 +587,14 @@ public class AuthenticationTest extends BaseTestSupport {
@Test // see SSHD-618
public void testPublicKeyAuthDifferentThanKex() throws Exception {
KeyPairProvider serverKeys = KeyPairProvider.wrap(
- Utils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024),
- Utils.generateKeyPair(KeyUtils.DSS_ALGORITHM, 512),
- Utils.generateKeyPair(KeyUtils.EC_ALGORITHM, 256));
+ CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024),
+ CommonTestSupportUtils.generateKeyPair(KeyUtils.DSS_ALGORITHM, 512),
+ CommonTestSupportUtils.generateKeyPair(KeyUtils.EC_ALGORITHM, 256));
sshd.setKeyPairProvider(serverKeys);
sshd.setKeyboardInteractiveAuthenticator(KeyboardInteractiveAuthenticator.NONE);
sshd.setPasswordAuthenticator(RejectAllPasswordAuthenticator.INSTANCE);
- final KeyPair clientIdentity = Utils.generateKeyPair(KeyUtils.EC_ALGORITHM, 256);
+ final KeyPair clientIdentity = CommonTestSupportUtils.generateKeyPair(KeyUtils.EC_ALGORITHM, 256);
sshd.setPublickeyAuthenticator((username, key, session) -> {
String keyType = KeyUtils.getKeyType(key);
String expType = KeyUtils.getKeyType(clientIdentity);
@@ -650,7 +650,7 @@ public class AuthenticationTest extends BaseTestSupport {
super.sendPublicKeyResponse(session, username, KeyPairProvider.SSH_DSS, key, keyBlob, offset, blobLen, buffer);
} else if (count == 2) {
// send another key
- KeyPair otherPair = org.apache.sshd.util.test.Utils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
+ KeyPair otherPair = org.apache.sshd.util.test.CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
PublicKey otherKey = otherPair.getPublic();
Buffer buf = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_PK_OK, blobLen + alg.length() + Long.SIZE);
buf.putString(alg);
@@ -666,7 +666,7 @@ public class AuthenticationTest extends BaseTestSupport {
}));
try (SshClient client = setupTestClient()) {
- KeyPair clientIdentity = Utils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
+ KeyPair clientIdentity = CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
client.start();
try {
@@ -693,7 +693,7 @@ public class AuthenticationTest extends BaseTestSupport {
public void testHostBasedAuthentication() throws Exception {
String hostClienUser = getClass().getSimpleName();
String hostClientName = SshdSocketAddress.toAddressString(SshdSocketAddress.getFirstExternalNetwork4Address());
- KeyPair hostClientKey = Utils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
+ KeyPair hostClientKey = CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
AtomicInteger invocationCount = new AtomicInteger(0);
sshd.setHostBasedAuthenticator((session, username, clientHostKey, clientHostName, clientUsername, certificates) -> {
invocationCount.incrementAndGet();
@@ -751,7 +751,7 @@ public class AuthenticationTest extends BaseTestSupport {
sshd.setKeyboardInteractiveAuthenticator(KeyboardInteractiveAuthenticator.NONE);
try (SshClient client = setupTestClient()) {
- KeyPair kp = Utils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
+ KeyPair kp = CommonTestSupportUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
client.start();
try {
for (int index = 1; index < 3; index++) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java
deleted file mode 100644
index 6611702..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java
+++ /dev/null
@@ -1,41 +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.cipher;
-
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class AES192CTRTest extends BaseCipherTest {
- public AES192CTRTest() {
- super();
- }
-
- @Test
- public void testEncryptDecrypt() throws Exception {
- // for AES 256 bits we need the JCE unlimited strength policy
- ensureKeySizeSupported(16, 24, "AES", "AES/CTR/NoPadding");
- testEncryptDecrypt(BuiltinCiphers.aes192ctr);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java
deleted file mode 100644
index dd39fd4..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java
+++ /dev/null
@@ -1,41 +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.cipher;
-
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class AES256CBCTest extends BaseCipherTest {
- public AES256CBCTest() {
- super();
- }
-
- @Test
- public void testEncryptDecrypt() throws Exception {
- // for AES 256 bits we need the JCE unlimited strength policy
- ensureKeySizeSupported(16, 32, "AES", "AES/CBC/NoPadding");
- testEncryptDecrypt(BuiltinCiphers.aes256cbc);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java
deleted file mode 100644
index 1c37449..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java
+++ /dev/null
@@ -1,39 +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.cipher;
-
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class ARCFOUR128Test extends BaseCipherTest {
- public ARCFOUR128Test() {
- super();
- }
-
- @Test
- public void testEncryptDecrypt() throws Exception {
- testEncryptDecrypt(BuiltinCiphers.arcfour128);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java
deleted file mode 100644
index 5511e0f..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java
+++ /dev/null
@@ -1,41 +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.cipher;
-
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class ARCFOUR256Test extends BaseCipherTest {
- public ARCFOUR256Test() {
- super();
- }
-
- @Test
- public void testEncryptDecrypt() throws Exception {
- // for RC4 256 bits we need the JCE unlimited strength policy
- ensureKeySizeSupported(32, "ARCFOUR", "RC4");
- testEncryptDecrypt(BuiltinCiphers.arcfour256);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java
deleted file mode 100644
index fd30e18..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java
+++ /dev/null
@@ -1,95 +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.cipher;
-
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.cipher.Cipher.Mode;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.experimental.categories.Category;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@Category({ NoIoTestCase.class })
-public abstract class BaseCipherTest extends BaseTestSupport {
- protected BaseCipherTest() {
- super();
- }
-
- protected void ensureKeySizeSupported(int bsize, String algorithm, String transformation) throws GeneralSecurityException {
- try {
- javax.crypto.Cipher cipher = SecurityUtils.getCipher(transformation);
- byte[] key = new byte[bsize];
- cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, new SecretKeySpec(key, algorithm));
- } catch (GeneralSecurityException e) {
- if (e instanceof InvalidKeyException) { // NOTE: assumption violations are NOT test failures...
- Assume.assumeTrue(algorithm + "/" + transformation + "[" + bsize + "] N/A", false);
- }
-
- throw e;
- }
- }
-
- protected void ensureKeySizeSupported(int ivsize, int bsize, String algorithm, String transformation) throws GeneralSecurityException {
- try {
- javax.crypto.Cipher cipher = SecurityUtils.getCipher(transformation);
- byte[] key = new byte[bsize];
- byte[] iv = new byte[ivsize];
- cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, new SecretKeySpec(key, algorithm), new IvParameterSpec(iv));
- } catch (GeneralSecurityException e) {
- if (e instanceof InvalidKeyException) {
- Assume.assumeTrue(algorithm + "/" + transformation + "[" + bsize + "/" + ivsize + "]", false /* force exception */);
- }
-
- throw e;
- }
- }
-
- protected void testEncryptDecrypt(NamedFactory<Cipher> factory) throws Exception {
- String facName = factory.getName();
- Cipher enc = factory.create();
- int keySize = enc.getBlockSize();
- int ivSize = enc.getIVSize();
- byte[] key = new byte[keySize];
- byte[] iv = new byte[ivSize];
- enc.init(Mode.Encrypt, key, iv);
-
- byte[] expected = facName.getBytes(StandardCharsets.UTF_8);
- byte[] workBuf = expected.clone(); // need to clone since the cipher works in-line
- enc.update(workBuf, 0, workBuf.length);
-
- Cipher dec = factory.create();
- dec.init(Mode.Decrypt, key, iv);
- byte[] actual = workBuf.clone();
- dec.update(actual, 0, actual.length);
-
- assertArrayEquals(facName, expected, actual);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
index 4a02074..0089782 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/cipher/CipherTest.java
@@ -35,10 +35,11 @@ import org.apache.sshd.common.random.Random;
import org.apache.sshd.common.util.buffer.BufferUtils;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.apache.sshd.util.test.JSchLogger;
import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
import org.apache.sshd.util.test.SimpleUserInfo;
-import org.apache.sshd.util.test.Utils;
import org.junit.AfterClass;
import org.junit.Assume;
import org.junit.BeforeClass;
@@ -67,15 +68,15 @@ public class CipherTest extends BaseTestSupport {
* NOTE !!! order is important since we build from it the C2S/S2C ciphers proposal
*/
private static final List<Object[]> PARAMETERS =
- Collections.unmodifiableList(Arrays.asList(
- new Object[]{BuiltinCiphers.aes128cbc, com.jcraft.jsch.jce.AES128CBC.class, NUM_LOADTEST_ROUNDS},
- new Object[]{BuiltinCiphers.tripledescbc, com.jcraft.jsch.jce.TripleDESCBC.class, NUM_LOADTEST_ROUNDS},
- new Object[]{BuiltinCiphers.blowfishcbc, com.jcraft.jsch.jce.BlowfishCBC.class, NUM_LOADTEST_ROUNDS},
- new Object[]{BuiltinCiphers.aes192cbc, com.jcraft.jsch.jce.AES192CBC.class, NUM_LOADTEST_ROUNDS},
- new Object[]{BuiltinCiphers.aes256cbc, com.jcraft.jsch.jce.AES256CBC.class, NUM_LOADTEST_ROUNDS},
- new Object[]{BuiltinCiphers.arcfour128, com.jcraft.jsch.jce.ARCFOUR128.class, NUM_LOADTEST_ROUNDS},
- new Object[]{BuiltinCiphers.arcfour256, com.jcraft.jsch.jce.ARCFOUR256.class, NUM_LOADTEST_ROUNDS}
- ));
+ Collections.unmodifiableList(Arrays.asList(
+ new Object[]{BuiltinCiphers.aes128cbc, com.jcraft.jsch.jce.AES128CBC.class, NUM_LOADTEST_ROUNDS},
+ new Object[]{BuiltinCiphers.tripledescbc, com.jcraft.jsch.jce.TripleDESCBC.class, NUM_LOADTEST_ROUNDS},
+ new Object[]{BuiltinCiphers.blowfishcbc, com.jcraft.jsch.jce.BlowfishCBC.class, NUM_LOADTEST_ROUNDS},
+ new Object[]{BuiltinCiphers.aes192cbc, com.jcraft.jsch.jce.AES192CBC.class, NUM_LOADTEST_ROUNDS},
+ new Object[]{BuiltinCiphers.aes256cbc, com.jcraft.jsch.jce.AES256CBC.class, NUM_LOADTEST_ROUNDS},
+ new Object[]{BuiltinCiphers.arcfour128, com.jcraft.jsch.jce.ARCFOUR128.class, NUM_LOADTEST_ROUNDS},
+ new Object[]{BuiltinCiphers.arcfour256, com.jcraft.jsch.jce.ARCFOUR256.class, NUM_LOADTEST_ROUNDS}
+ ));
private static final List<NamedResource> TEST_CIPHERS =
Collections.unmodifiableList(
@@ -86,7 +87,7 @@ public class CipherTest extends BaseTestSupport {
private static SshServer sshd;
private static int port;
- private final Random random = Utils.getRandomizerInstance();
+ private final Random random = CommonTestSupportUtils.getRandomizerInstance();
private final BuiltinCiphers builtInCipher;
private final Class<? extends com.jcraft.jsch.Cipher> jschCipher;
private final int loadTestRounds;
@@ -105,7 +106,7 @@ public class CipherTest extends BaseTestSupport {
@BeforeClass
public static void setupClientAndServer() throws Exception {
JSchLogger.init();
- sshd = Utils.setupTestServer(CipherTest.class);
+ sshd = CoreTestSupportUtils.setupTestServer(CipherTest.class);
sshd.start();
port = sshd.getPort();
}
[09/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java b/sshd-core/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java
deleted file mode 100644
index 7df1c64..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java
+++ /dev/null
@@ -1,107 +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.cipher;
-
-import java.util.EnumSet;
-import java.util.Set;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class ECCurvesTest extends BaseTestSupport {
- public ECCurvesTest() {
- super();
- }
-
- @Test
- public void testFromName() {
- for (ECCurves expected : ECCurves.VALUES) {
- String name = expected.getName();
- for (int index = 0; index < name.length(); index++) {
- ECCurves actual = ECCurves.fromCurveName(name);
- assertSame(name, expected, actual);
- name = shuffleCase(name);
- }
- }
- }
-
- @Test
- public void testAllNamesListed() {
- Set<ECCurves> listed = EnumSet.noneOf(ECCurves.class);
- for (String name : ECCurves.NAMES) {
- ECCurves c = ECCurves.fromCurveName(name);
- assertNotNull("No curve for listed name=" + name, c);
- assertTrue("Duplicated listed name: " + name, listed.add(c));
- }
-
- assertEquals("Mismatched listed vs. values", ECCurves.VALUES, listed);
- }
-
- @Test
- public void testFromKeySize() {
- for (ECCurves expected : ECCurves.VALUES) {
- String name = expected.getName();
- ECCurves actual = ECCurves.fromCurveSize(expected.getKeySize());
- assertSame(name, expected, actual);
- }
- }
-
- @Test
- public void testFromCurveParameters() {
- for (ECCurves expected : ECCurves.VALUES) {
- String name = expected.getName();
- ECCurves actual = ECCurves.fromCurveParameters(expected.getParameters());
- assertSame(name, expected, actual);
- }
- }
-
- @Test
- public void testFromKeyType() {
- for (ECCurves expected : ECCurves.VALUES) {
- String keyType = expected.getKeyType();
- for (int index = 0; index < keyType.length(); index++) {
- ECCurves actual = ECCurves.fromKeyType(keyType);
- assertSame(keyType, expected, actual);
- keyType = shuffleCase(keyType);
- }
- }
- }
-
- @Test
- public void testAllKeyTypesListed() {
- Set<ECCurves> listed = EnumSet.noneOf(ECCurves.class);
- for (String name : ECCurves.KEY_TYPES) {
- ECCurves c = ECCurves.fromKeyType(name);
- assertNotNull("No curve for listed key type=" + name, c);
- assertTrue("Duplicated listed key type: " + name, listed.add(c));
- }
-
- assertEquals("Mismatched listed vs. values", ECCurves.VALUES, listed);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
deleted file mode 100644
index 0daeb23..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java
+++ /dev/null
@@ -1,171 +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.compression;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.compression.BuiltinCompressions.ParseResult;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-import org.mockito.Mockito;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class BuiltinCompressionsTest extends BaseTestSupport {
- public BuiltinCompressionsTest() {
- super();
- }
-
- @Test
- public void testFromFactoryName() {
- for (BuiltinCompressions expected : BuiltinCompressions.VALUES) {
- String name = expected.getName();
-
- for (int index = 0; index < name.length(); index++) {
- BuiltinCompressions actual = BuiltinCompressions.fromFactoryName(name);
- assertSame(name, expected, actual);
- name = shuffleCase(name);
- }
- }
- }
-
- @Test
- public void testAllConstantsCovered() throws Exception {
- Set<BuiltinCompressions> avail = EnumSet.noneOf(BuiltinCompressions.class);
- Field[] fields = BuiltinCompressions.Constants.class.getFields();
- for (Field f : fields) {
- String name = (String) f.get(null);
- BuiltinCompressions value = BuiltinCompressions.fromFactoryName(name);
- assertNotNull("No match found for " + name, value);
- assertTrue(name + " re-specified", avail.add(value));
- }
-
- assertEquals("Incomplete coverage", BuiltinCompressions.VALUES, avail);
- }
-
- @Test
- public void testParseCompressionsList() {
- List<String> builtin = NamedResource.getNameList(BuiltinCompressions.VALUES);
- List<String> unknown = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
- Random rnd = new Random();
- for (int index = 0; index < (builtin.size() + unknown.size()); index++) {
- Collections.shuffle(builtin, rnd);
- Collections.shuffle(unknown, rnd);
-
- List<String> weavedList = new ArrayList<>(builtin.size() + unknown.size());
- for (int bIndex = 0, uIndex = 0; (bIndex < builtin.size()) || (uIndex < unknown.size());) {
- boolean useBuiltin = false;
- if (bIndex < builtin.size()) {
- useBuiltin = uIndex >= unknown.size() || rnd.nextBoolean();
- }
-
- if (useBuiltin) {
- weavedList.add(builtin.get(bIndex));
- bIndex++;
- } else if (uIndex < unknown.size()) {
- weavedList.add(unknown.get(uIndex));
- uIndex++;
- }
- }
-
- String fullList = GenericUtils.join(weavedList, ',');
- ParseResult result = BuiltinCompressions.parseCompressionsList(fullList);
- List<String> parsed = NamedResource.getNameList(result.getParsedFactories());
- List<String> missing = result.getUnsupportedFactories();
-
- // makes sure not only that the contents are the same but also the order
- assertListEquals(fullList + "[parsed]", builtin, parsed);
- assertListEquals(fullList + "[unsupported]", unknown, missing);
- }
- }
-
- @Test
- public void testResolveFactoryOnBuiltinValues() {
- for (NamedFactory<Compression> expected : BuiltinCompressions.VALUES) {
- String name = expected.getName();
- NamedFactory<Compression> actual = BuiltinCompressions.resolveFactory(name);
- assertSame(name, expected, actual);
- }
- }
-
- @Test
- public void testNotAllowedToRegisterBuiltinFactories() {
- for (CompressionFactory expected : BuiltinCompressions.VALUES) {
- try {
- BuiltinCompressions.registerExtension(expected);
- fail("Unexpected success for " + expected.getName());
- } catch (IllegalArgumentException e) {
- // expected - ignored
- }
- }
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testNotAllowedToOverrideRegisteredFactories() {
- CompressionFactory expected = Mockito.mock(CompressionFactory.class);
- Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
-
- String name = expected.getName();
- try {
- for (int index = 1; index <= Byte.SIZE; index++) {
- BuiltinCompressions.registerExtension(expected);
- assertEquals("Unexpected success at attempt #" + index, 1, index);
- }
- } finally {
- BuiltinCompressions.unregisterExtension(name);
- }
- }
-
- @Test
- public void testResolveFactoryOnRegisteredExtension() {
- CompressionFactory expected = Mockito.mock(CompressionFactory.class);
- Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
-
- String name = expected.getName();
- try {
- assertNull("Extension already registered", BuiltinCompressions.resolveFactory(name));
- BuiltinCompressions.registerExtension(expected);
-
- NamedFactory<Compression> actual = BuiltinCompressions.resolveFactory(name);
- assertSame("Mismatched resolved instance", expected, actual);
- } finally {
- NamedFactory<Compression> actual = BuiltinCompressions.unregisterExtension(name);
- assertSame("Mismatched unregistered instance", expected, actual);
- assertNull("Extension not un-registered", BuiltinCompressions.resolveFactory(name));
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/compression/CompressionTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/compression/CompressionTest.java b/sshd-core/src/test/java/org/apache/sshd/common/compression/CompressionTest.java
index a8132fb..b18c273 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/compression/CompressionTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/compression/CompressionTest.java
@@ -33,10 +33,11 @@ import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.session.SessionListener;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
import org.apache.sshd.util.test.JSchLogger;
import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
import org.apache.sshd.util.test.SimpleUserInfo;
-import org.apache.sshd.util.test.Utils;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assume;
@@ -96,8 +97,8 @@ public class CompressionTest extends BaseTestSupport {
public static void setupClientAndServer() throws Exception {
JSchLogger.init();
- sshd = Utils.setupTestServer(MacTest.class);
- sshd.setKeyPairProvider(Utils.createTestHostKeyProvider(MacTest.class));
+ sshd = CoreTestSupportUtils.setupTestServer(MacTest.class);
+ sshd.setKeyPairProvider(CommonTestSupportUtils.createTestHostKeyProvider(MacTest.class));
sshd.start();
port = sshd.getPort();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java
index f9de9b1..94ce291 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java
@@ -71,7 +71,7 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
URL url = getClass().getResource("sshd_config");
assertNotNull("Cannot locate test file", url);
- Properties props = SshConfigFileReader.readConfigFile(url);
+ Properties props = ConfigFileReaderSupport.readConfigFile(url);
assertFalse("No properties read", props.isEmpty());
assertTrue("Unexpected commented property data", GenericUtils.isEmpty(props.getProperty("ListenAddress")));
assertTrue("Unexpected non-existing property data", GenericUtils.isEmpty(props.getProperty(getCurrentTestName())));
@@ -86,55 +86,55 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
@Test
public void testParseCiphersList() {
List<? extends NamedResource> expected = BaseBuilder.DEFAULT_CIPHERS_PREFERENCE;
- Properties props = initNamedResourceProperties(SshConfigFileReader.CIPHERS_CONFIG_PROP, expected);
+ Properties props = initNamedResourceProperties(ConfigFileReaderSupport.CIPHERS_CONFIG_PROP, expected);
BuiltinCiphers.ParseResult result = SshConfigFileReader.getCiphers(PropertyResolverUtils.toPropertyResolver(props));
testParsedFactoriesList(expected, result.getParsedFactories(), result.getUnsupportedFactories());
}
@Test
public void testKnownDefaultCipherFactoriesList() {
- testKnownDefaultFactoriesList(SshConfigFileReader.DEFAULT_CIPHERS, BuiltinCiphers::fromFactoryName,
+ testKnownDefaultFactoriesList(ConfigFileReaderSupport.DEFAULT_CIPHERS, BuiltinCiphers::fromFactoryName,
GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER, "cast128-cbc", "arcfour"));
}
@Test
public void testParseMacsList() {
List<? extends NamedResource> expected = BaseBuilder.DEFAULT_MAC_PREFERENCE;
- Properties props = initNamedResourceProperties(SshConfigFileReader.MACS_CONFIG_PROP, expected);
+ Properties props = initNamedResourceProperties(ConfigFileReaderSupport.MACS_CONFIG_PROP, expected);
BuiltinMacs.ParseResult result = SshConfigFileReader.getMacs(PropertyResolverUtils.toPropertyResolver(props));
testParsedFactoriesList(expected, result.getParsedFactories(), result.getUnsupportedFactories());
}
@Test
public void testKnownDefaultMacFactoriesList() {
- testKnownDefaultFactoriesList(SshConfigFileReader.DEFAULT_MACS, BuiltinMacs::fromFactoryName,
+ testKnownDefaultFactoriesList(ConfigFileReaderSupport.DEFAULT_MACS, BuiltinMacs::fromFactoryName,
GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER, "umac-64@openssh.com", "hmac-ripemd160", "hmac-sha2-256-96", "hmac-sha2-512-96"));
}
@Test
public void testParseSignaturesList() {
List<? extends NamedResource> expected = BaseBuilder.DEFAULT_SIGNATURE_PREFERENCE;
- Properties props = initNamedResourceProperties(SshConfigFileReader.HOST_KEY_ALGORITHMS_CONFIG_PROP, expected);
+ Properties props = initNamedResourceProperties(ConfigFileReaderSupport.HOST_KEY_ALGORITHMS_CONFIG_PROP, expected);
BuiltinSignatures.ParseResult result = SshConfigFileReader.getSignatures(PropertyResolverUtils.toPropertyResolver(props));
testParsedFactoriesList(expected, result.getParsedFactories(), result.getUnsupportedFactories());
}
@Test
public void testKnownDefaultSignatureFactoriesList() {
- testKnownDefaultFactoriesList(SshConfigFileReader.DEFAULT_HOST_KEY_ALGORITHMS, BuiltinSignatures::fromFactoryName);
+ testKnownDefaultFactoriesList(ConfigFileReaderSupport.DEFAULT_HOST_KEY_ALGORITHMS, BuiltinSignatures::fromFactoryName);
}
@Test
public void testParseKexFactoriesList() {
List<? extends NamedResource> expected = BaseBuilder.DEFAULT_KEX_PREFERENCE;
- Properties props = initNamedResourceProperties(SshConfigFileReader.KEX_ALGORITHMS_CONFIG_PROP, expected);
+ Properties props = initNamedResourceProperties(ConfigFileReaderSupport.KEX_ALGORITHMS_CONFIG_PROP, expected);
BuiltinDHFactories.ParseResult result = SshConfigFileReader.getKexFactories(PropertyResolverUtils.toPropertyResolver(props));
testParsedFactoriesList(expected, result.getParsedFactories(), result.getUnsupportedFactories());
}
@Test
public void testKnownDefaultKexFactoriesList() {
- testKnownDefaultFactoriesList(SshConfigFileReader.DEFAULT_KEX_ALGORITHMS, BuiltinDHFactories::fromFactoryName);
+ testKnownDefaultFactoriesList(ConfigFileReaderSupport.DEFAULT_KEX_ALGORITHMS, BuiltinDHFactories::fromFactoryName);
}
private static void testKnownDefaultFactoriesList(String factories, Function<? super String, ? extends NamedResource> resolver) {
@@ -159,7 +159,7 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
public void testGetCompression() {
Properties props = new Properties();
for (CompressionConfigValue expected : CompressionConfigValue.VALUES) {
- props.setProperty(SshConfigFileReader.COMPRESSION_PROP, expected.name().toLowerCase());
+ props.setProperty(ConfigFileReaderSupport.COMPRESSION_PROP, expected.name().toLowerCase());
NamedResource actual = SshConfigFileReader.getCompression(PropertyResolverUtils.toPropertyResolver(props));
assertNotNull("No match for " + expected.name(), actual);
@@ -177,7 +177,8 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
}
};
// must be lenient since we do not cover the full default spectrum
- AbstractFactoryManager actual = SshConfigFileReader.configure(expected, PropertyResolverUtils.toPropertyResolver(props), true, true);
+ AbstractFactoryManager actual = SshConfigFileReader.configure(
+ expected, PropertyResolverUtils.toPropertyResolver(props), true, true);
assertSame("Mismatched configured result", expected, actual);
validateAbstractFactoryManagerConfiguration(expected, props, true);
}
@@ -237,7 +238,7 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
public void testInvalidDelimiter() throws IOException {
String line = getClass().getSimpleName() + "+" + getCurrentTestName();
try (Reader rdr = new StringReader(line)) {
- Properties props = SshConfigFileReader.readConfigFile(rdr, true);
+ Properties props = ConfigFileReaderSupport.readConfigFile(rdr, true);
fail("Unexpected success: " + props);
}
}
@@ -248,7 +249,7 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
String expected = getCurrentTestName();
Properties props;
try (Reader rdr = new StringReader(name + "\t" + expected)) {
- props = SshConfigFileReader.readConfigFile(rdr, true);
+ props = ConfigFileReaderSupport.readConfigFile(rdr, true);
}
String actual = props.getProperty(name);
@@ -285,7 +286,8 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
}
private static <M extends FactoryManager> M validateFactoryManagerCiphers(M manager, Properties props) {
- return validateFactoryManagerCiphers(manager, props.getProperty(SshConfigFileReader.CIPHERS_CONFIG_PROP, SshConfigFileReader.DEFAULT_CIPHERS));
+ return validateFactoryManagerCiphers(manager,
+ props.getProperty(ConfigFileReaderSupport.CIPHERS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_CIPHERS));
}
private static <M extends FactoryManager> M validateFactoryManagerCiphers(M manager, String value) {
@@ -295,7 +297,8 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
}
private static <M extends FactoryManager> M validateFactoryManagerSignatures(M manager, Properties props) {
- return validateFactoryManagerSignatures(manager, props.getProperty(SshConfigFileReader.HOST_KEY_ALGORITHMS_CONFIG_PROP, SshConfigFileReader.DEFAULT_HOST_KEY_ALGORITHMS));
+ return validateFactoryManagerSignatures(manager,
+ props.getProperty(ConfigFileReaderSupport.HOST_KEY_ALGORITHMS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_HOST_KEY_ALGORITHMS));
}
private static <M extends FactoryManager> M validateFactoryManagerSignatures(M manager, String value) {
@@ -305,7 +308,8 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
}
private static <M extends FactoryManager> M validateFactoryManagerMacs(M manager, Properties props) {
- return validateFactoryManagerMacs(manager, props.getProperty(SshConfigFileReader.MACS_CONFIG_PROP, SshConfigFileReader.DEFAULT_MACS));
+ return validateFactoryManagerMacs(manager,
+ props.getProperty(ConfigFileReaderSupport.MACS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_MACS));
}
private static <M extends FactoryManager> M validateFactoryManagerMacs(M manager, String value) {
@@ -315,7 +319,8 @@ public class SshConfigFileReaderTest extends BaseTestSupport {
}
private static <M extends FactoryManager> M validateFactoryManagerCompressions(M manager, Properties props, boolean lenient) {
- return validateFactoryManagerCompressions(manager, props.getProperty(SshConfigFileReader.COMPRESSION_PROP, SshConfigFileReader.DEFAULT_COMPRESSION), lenient);
+ return validateFactoryManagerCompressions(manager,
+ props.getProperty(ConfigFileReaderSupport.COMPRESSION_PROP, ConfigFileReaderSupport.DEFAULT_COMPRESSION), lenient);
}
private static <M extends FactoryManager> M validateFactoryManagerCompressions(M manager, String value, boolean lenient) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java
deleted file mode 100644
index e9acf4d..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java
+++ /dev/null
@@ -1,57 +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.concurrent.TimeUnit;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class TimeValueConfigTest extends BaseTestSupport {
- public TimeValueConfigTest() {
- super();
- }
-
- @Test
- public void testDurationOf() {
- Object[] values = {
- "600", TimeUnit.SECONDS.toMillis(600L),
- "10m", TimeUnit.MINUTES.toMillis(10L),
- "1h30m", TimeUnit.MINUTES.toMillis(90L),
- "2d", TimeUnit.DAYS.toMillis(2L),
- "3w", TimeUnit.DAYS.toMillis(3L * 7L)
- };
- for (int index = 0; index < values.length; index += 2) {
- String s = (String) values[index];
- Number expected = (Number) values[index + 1];
- long actual = TimeValueConfig.durationOf(s);
- assertEquals(s, expected.longValue(), actual);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java
deleted file mode 100644
index 5588622..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java
+++ /dev/null
@@ -1,124 +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.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-public class AuthorizedKeyEntryLoginOptionsParseTest extends BaseTestSupport {
- private final String value;
- private final String loginPart;
- private final String keyPart;
- private final Map<String, String> options;
-
- public AuthorizedKeyEntryLoginOptionsParseTest(String value, String loginPart, String keyPart, Map<String, String> options) {
- this.value = value;
- this.loginPart = loginPart;
- this.keyPart = keyPart;
- this.options = options;
- }
-
- @Parameters(name = "{0}")
- public static List<Object[]> parameters() {
- List<Object[]> params = new ArrayList<>();
- addData(params, "ssh-rsa AAAAB2...19Q==", "john@example.net", "from=\"*.sales.example.net,!pc.sales.example.net\"");
- addData(params, "ssh-dss AAAAC3...51R==", "example.net", "command=\"dump /home\"", "no-pty", "no-port-forwarding");
- addData(params, "ssh-dss AAAAB5...21S==", "", "permitopen=\"192.0.2.1:80\"", "permitopen=\"192.0.2.2:25\"");
- addData(params, "ssh-rsa AAAA...==", "jane@example.net", "tunnel=\"0\"", "command=\"sh /etc/netstart tun0\"");
- addData(params, "ssh-rsa AAAA1C8...32Tv==", "user@example.net", "!restrict", "command=\"uptime\"");
- addData(params, "ssh-rsa AAAA1f8...IrrC5==", "user@example.net", "restrict", "!pty", "command=\"nethack\"");
- return params;
- }
-
- private static void addData(List<Object[]> params, String keyData, String comment, String... comps) {
- StringBuilder sb = new StringBuilder();
-
- Map<String, String> optionsMap = new HashMap<>();
- for (String c : comps) {
- if (sb.length() > 0) {
- sb.append(',');
- }
- sb.append(c);
-
- int pos = c.indexOf('=');
- if (pos > 0) {
- String name = c.substring(0, pos);
- String value = GenericUtils.stripQuotes(c.substring(pos + 1)).toString();
- String prev = optionsMap.put(name, value);
- if (prev != null) {
- optionsMap.put(name, prev + "," + value);
- }
- } else {
- optionsMap.put(c, Boolean.toString(c.charAt(0) != AuthorizedKeyEntry.BOOLEAN_OPTION_NEGATION_INDICATOR));
- }
- }
-
- int pos = sb.length();
-
- sb.append(' ').append(keyData);
- if (GenericUtils.isNotEmpty(comment)) {
- sb.append(' ').append(comment);
- }
-
- String value = sb.toString();
- params.add(new Object[] {value, value.substring(0, pos), value.substring(pos + 1), optionsMap});
- }
-
- @Test
- public void testResolveEntryComponents() {
- Map.Entry<String, String> actual = AuthorizedKeyEntry.resolveEntryComponents(value);
- assertNotNull(value, actual);
- assertEquals("login(" + value + ")", loginPart, actual.getKey());
- assertEquals("remainder(" + value + ")", keyPart, actual.getValue());
- }
-
- @Test
- public void testParseLoginOptions() {
- Map<String, String> parsed = AuthorizedKeyEntry.parseLoginOptions(loginPart);
- options.forEach((key, expected) -> {
- String actual = parsed.get(key);
- assertEquals(key, expected, actual);
- });
- assertEquals("Mismatched size", options.size(), parsed.size());
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + "[" + value + "]";
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryTest.java
index d28e3df..2b0dec4 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryTest.java
@@ -123,8 +123,10 @@ public class AuthorizedKeyEntryTest extends AuthorizedKeysTestSupport {
}
private PublickeyAuthenticator testAuthorizedKeysAuth(Collection<AuthorizedKeyEntry> entries) throws Exception {
- Collection<PublicKey> keySet = AuthorizedKeyEntry.resolveAuthorizedKeys(PublicKeyEntryResolver.FAILING, entries);
- PublickeyAuthenticator auth = AuthorizedKeyEntry.fromAuthorizedEntries(PublicKeyEntryResolver.FAILING, entries);
+ Collection<PublicKey> keySet =
+ AuthorizedKeyEntry.resolveAuthorizedKeys(PublicKeyEntryResolver.FAILING, entries);
+ PublickeyAuthenticator auth =
+ PublickeyAuthenticator.fromAuthorizedEntries(PublicKeyEntryResolver.FAILING, entries);
for (PublicKey key : keySet) {
assertTrue("Failed to authenticate with key=" + key.getAlgorithm(), auth.authenticate(getCurrentTestName(), key, null));
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java
deleted file mode 100644
index acc349a..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java
+++ /dev/null
@@ -1,116 +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.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.util.List;
-
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.BeforeClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class BuiltinIdentitiesTest extends BaseTestSupport {
- private final BuiltinIdentities expected;
-
- public BuiltinIdentitiesTest(BuiltinIdentities expected) {
- this.expected = expected;
- }
-
- @Parameters(name = "{0}")
- public static List<Object[]> parameters() {
- return parameterize(BuiltinIdentities.VALUES);
- }
-
- @BeforeClass // Dirty hack around the parameterized run
- public static void testAllConstantsCovered() throws Exception {
- Field[] fields = BuiltinIdentities.Constants.class.getFields();
- for (Field f : fields) {
- int mods = f.getModifiers();
- if (!Modifier.isStatic(mods)) {
- continue;
- }
-
- if (!Modifier.isFinal(mods)) {
- continue;
- }
-
- Class<?> type = f.getType();
- if (!String.class.isAssignableFrom(type)) {
- continue;
- }
-
- String name = f.getName();
- String value = (String) f.get(null);
- BuiltinIdentities id = BuiltinIdentities.fromName(value);
- assertNotNull("No match found for field " + name + "=" + value, id);
- }
- }
-
- @Test
- public void testFromName() {
- String name = expected.getName();
- for (int index = 0, count = name.length(); index < count; index++) {
- assertSame(name, expected, BuiltinIdentities.fromName(name));
- name = shuffleCase(name);
- }
- }
-
- @Test
- public void testFromAlgorithm() {
- String algorithm = expected.getAlgorithm();
- for (int index = 0, count = algorithm.length(); index < count; index++) {
- assertSame(algorithm, expected, BuiltinIdentities.fromAlgorithm(algorithm));
- algorithm = shuffleCase(algorithm);
- }
- }
-
- @Test
- public void testFromKey() throws GeneralSecurityException {
- Assume.assumeTrue("Unsupported built-in identity", expected.isSupported());
- KeyPairGenerator gen = SecurityUtils.getKeyPairGenerator(expected.getAlgorithm());
- KeyPair kp = gen.generateKeyPair();
- outputDebugMessage("Checking built-in identity: %s", expected);
- assertSame(expected + "[pair]", expected, BuiltinIdentities.fromKeyPair(kp));
- assertSame(expected + "[public]", expected, BuiltinIdentities.fromKey(kp.getPublic()));
- assertSame(expected + "[private]", expected, BuiltinIdentities.fromKey(kp.getPrivate()));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java
deleted file mode 100644
index 4c74ef5..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java
+++ /dev/null
@@ -1,117 +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.KeyPair;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.apache.sshd.util.test.Utils;
-import org.junit.AfterClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class KeyRandomArtTest extends BaseTestSupport {
- private static final Collection<KeyPair> KEYS = new LinkedList<>();
-
- private final String algorithm;
- private final int keySize;
- private final KeyPair keyPair;
-
- public KeyRandomArtTest(String algorithm, int keySize) throws Exception {
- this.algorithm = algorithm;
- this.keySize = keySize;
- this.keyPair = Utils.generateKeyPair(algorithm, keySize);
- KEYS.add(this.keyPair);
- }
-
- @Parameters(name = "algorithm={0}, key-size={1}")
- public static List<Object[]> parameters() {
- List<Object[]> params = new ArrayList<>();
- for (int keySize : RSA_SIZES) {
- params.add(new Object[]{KeyUtils.RSA_ALGORITHM, keySize});
- }
-
- for (int keySize : DSS_SIZES) {
- params.add(new Object[]{KeyUtils.DSS_ALGORITHM, keySize});
- }
-
- if (SecurityUtils.isECCSupported()) {
- for (ECCurves curve : ECCurves.VALUES) {
- params.add(new Object[]{KeyUtils.EC_ALGORITHM, curve.getKeySize()});
- }
- }
-
- if (SecurityUtils.isEDDSACurveSupported()) {
- for (int keySize : ED25519_SIZES) {
- params.add(new Object[]{SecurityUtils.EDDSA, keySize});
- }
- }
- return params;
- }
-
- @AfterClass
- public static void dumpAllArts() throws Exception {
- KeyRandomArt.combine(System.out, ' ', () -> KEYS);
- }
-
- @Test
- public void testRandomArtString() throws Exception {
- KeyRandomArt art = new KeyRandomArt(keyPair.getPublic());
- assertEquals("Mismatched algorithm", algorithm, art.getAlgorithm());
- assertEquals("Mismatched key size", keySize, art.getKeySize());
-
- String s = art.toString();
- String[] lines = GenericUtils.split(s, '\n');
- assertEquals("Mismatched lines count", KeyRandomArt.FLDSIZE_Y + 2, lines.length);
-
- for (int index = 0; index < lines.length; index++) {
- String l = lines[index];
- if ((l.length() > 0) && (l.charAt(l.length() - 1) == '\r')) {
- l = l.substring(0, l.length() - 1);
- lines[index] = l;
- }
- System.out.append('\t').println(l);
-
- assertTrue("Mismatched line length #" + (index + 1) + ": " + l.length(), l.length() >= (KeyRandomArt.FLDSIZE_X + 2));
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java
deleted file mode 100644
index cb23591..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsCloneTest.java
+++ /dev/null
@@ -1,120 +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.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class KeyUtilsCloneTest extends BaseTestSupport {
- private final String keyType;
- private final int keySize;
-
- public KeyUtilsCloneTest(String keyType, int keySize) {
- this.keyType = ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type specified");
- this.keySize = keySize;
- }
-
- @Parameters(name = "type={0}, size={1}")
- public static List<Object[]> parameters() {
- List<Object[]> list = new ArrayList<>();
- addTests(list, KeyPairProvider.SSH_DSS, DSS_SIZES);
- addTests(list, KeyPairProvider.SSH_RSA, RSA_SIZES);
- if (SecurityUtils.isECCSupported()) {
- for (ECCurves curve : ECCurves.VALUES) {
- if (!curve.isSupported()) {
- continue;
- }
- addTests(list, curve.getKeyType(), Collections.singletonList(curve.getKeySize()));
- }
- }
- if (SecurityUtils.isEDDSACurveSupported()) {
- addTests(list, KeyPairProvider.SSH_ED25519, ED25519_SIZES);
- }
- return Collections.unmodifiableList(list);
- }
-
- private static void addTests(List<Object[]> list, String keyType, Collection<Integer> sizes) {
- for (Integer keySize : sizes) {
- list.add(new Object[]{keyType, keySize});
- }
- }
-
- @Test
- @SuppressWarnings("checkstyle:avoidnestedblocks")
- public void testKeyPairCloning() throws GeneralSecurityException {
- outputDebugMessage("generateKeyPair(%s)[%d]", keyType, keySize);
- KeyPair original = KeyUtils.generateKeyPair(keyType, keySize);
-
- outputDebugMessage("cloneKeyPair(%s)[%d]", keyType, keySize);
- KeyPair cloned = KeyUtils.cloneKeyPair(keyType, original);
-
- String prefix = keyType + "[" + keySize + "]";
- assertNotSame(prefix + ": Key pair not cloned", original, cloned);
- assertTrue(prefix + ": Cloned pair not equals", KeyUtils.compareKeyPairs(original, cloned));
-
- {
- PublicKey k1 = original.getPublic();
- PublicKey k2 = cloned.getPublic();
- assertNotSame(prefix + ": Public key not cloned", k1, k2);
- assertTrue(prefix + ": Cloned public key not equals", KeyUtils.compareKeys(k1, k2));
-
- String f1 = KeyUtils.getFingerPrint(k1);
- String f2 = KeyUtils.getFingerPrint(k2);
- assertEquals(prefix + ": Mismatched fingerprints", f1, f2);
- }
-
- {
- PrivateKey k1 = original.getPrivate();
- PrivateKey k2 = cloned.getPrivate();
- assertNotSame(prefix + ": Private key not cloned", k1, k2);
- assertTrue(prefix + ": Cloned private key not equals", KeyUtils.compareKeys(k1, k2));
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintCaseSensitivityTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintCaseSensitivityTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintCaseSensitivityTest.java
deleted file mode 100644
index c9457fa..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintCaseSensitivityTest.java
+++ /dev/null
@@ -1,96 +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.security.GeneralSecurityException;
-import java.security.PublicKey;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Arrays;
-import java.util.Collection;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.BeforeClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class KeyUtilsFingerprintCaseSensitivityTest extends BaseTestSupport {
-
- // CHECKSTYLE:OFF
- private static final String KEY_STRING = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2ctGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZBgIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlEaaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwhV+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMw== lgoldstein@LGOLDSTEIN-WIN7";
- // CHECKSTYLE:ON
- private static final String MD5_PREFIX = "MD5:";
- private static final String MD5 = "24:32:3c:80:01:b3:e1:fa:7c:53:ca:e3:e8:4e:c6:8e";
- private static final String MD5_FULL = MD5_PREFIX + MD5;
- private static final String SHA1_PREFIX = "SHA1:";
- private static final String SHA1 = "ZNLzC6u+F37oq8BpEAwP69EQtoA";
- private static final String SHA1_FULL = SHA1_PREFIX + SHA1;
-
- private static PublicKey key;
-
- private String expected;
- private String test;
-
- public KeyUtilsFingerprintCaseSensitivityTest(String expected, String test) {
- this.expected = expected;
- this.test = test;
- }
-
- @BeforeClass
- public static void beforeClass() throws GeneralSecurityException, IOException {
- key = PublicKeyEntry.parsePublicKeyEntry(KEY_STRING).resolvePublicKey(PublicKeyEntryResolver.FAILING);
- }
-
- @Parameters(name = "expected={0}, test={1}")
- public static Collection<Object[]> parameters() {
- return Arrays.asList(
- new Object[] {MD5_FULL, MD5_FULL},
- new Object[] {MD5_FULL, MD5_FULL.toUpperCase()},
- new Object[] {MD5_FULL, MD5_FULL.toLowerCase()},
- new Object[] {MD5_FULL, MD5_PREFIX.toUpperCase() + MD5},
- new Object[] {MD5_FULL, MD5_PREFIX.toLowerCase() + MD5},
- new Object[] {MD5_FULL, MD5.toLowerCase()},
- new Object[] {MD5_FULL, MD5.toUpperCase()},
- new Object[] {SHA1_FULL, SHA1_FULL},
- new Object[] {SHA1_FULL, SHA1_PREFIX.toUpperCase() + SHA1},
- new Object[] {SHA1_FULL, SHA1_PREFIX.toLowerCase() + SHA1}
- );
- }
-
- @Test
- public void testCase() throws Exception {
- assertEquals("Check failed", new SimpleImmutableEntry<>(true, expected), KeyUtils.checkFingerPrint(test, key));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintGenerationTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintGenerationTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintGenerationTest.java
deleted file mode 100644
index e78eaeb..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsFingerprintGenerationTest.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.security.GeneralSecurityException;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.sshd.common.digest.BuiltinDigests;
-import org.apache.sshd.common.digest.DigestFactory;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class KeyUtilsFingerprintGenerationTest extends BaseTestSupport {
- private final PublicKey key;
- private final DigestFactory digestFactory;
- private final String expected;
-
- public KeyUtilsFingerprintGenerationTest(PublicKey key, DigestFactory digestFactory, String expected) {
- this.key = key;
- this.digestFactory = digestFactory;
- this.expected = expected;
- }
-
- @Parameters(name = "key={0}, digestFactory={1}, expected={2}")
- public static Collection<Object[]> parameters() throws IOException, GeneralSecurityException {
- List<? extends Map.Entry<String, List<? extends Map.Entry<DigestFactory, String>>>> keyEntries =
- Collections.unmodifiableList(Arrays.asList(
- new SimpleImmutableEntry<>(
- // CHECKSTYLE:OFF
- "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2ctGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZBgIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlEaaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwhV+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMw== lgoldstein@LGOLDSTEIN-WIN7",
- // CHECKSTYLE:ON
- Arrays.asList(
- new SimpleImmutableEntry<>(
- BuiltinDigests.md5,
- "MD5:24:32:3c:80:01:b3:e1:fa:7c:53:ca:e3:e8:4e:c6:8e"
- ),
- new SimpleImmutableEntry<>(
- BuiltinDigests.sha256,
- "SHA256:1wNOZO+/XgNGJMx8UUJst33V+bBMTz5EcL0B6y2iRv0"
- )
- )
- ),
- new SimpleImmutableEntry<>(
- // CHECKSTYLE:OFF
- "ssh-dss AAAAB3NzaC1kc3MAAACBAMg/IxsG5BxnF5gM7IKqqR0rftxZC+n5GlbO+J4H+iIb/KR8NBehkxG3CrBZMF96M2K1sEGYLob+3k4r71oWaPul8n5rt9kpd+JSq4iD2ygOyg6Kd1/YDBHoxneizy6I/bGsLwhAAKWcRNrXmYVKGzhrhvZWN12AJDq2mGdj3szLAAAAFQD7a2MltdUSF7FU3//SpW4WGjZbeQAAAIBf0nNsfKQL/TEMo7IpTrEMg5V0RnSigCX0+yUERS42GW/ZeCZBJw7oL2XZbuBtu63vMjDgVpnb92BdrcPgjJ7EFW6DlcyeuywStmg1ygXmDR2AQCxv0eX2CQgrdUczmRa155SDVUTvTQlO1IyKx0vwKAh1H7E3yJUfkTAJstbGYQAAAIEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+abYpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLaXWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7A= dsa-key-20130709",
- // CHECKSTYLE:ON
- Arrays.asList(
- new SimpleImmutableEntry<>(
- BuiltinDigests.md5,
- "MD5:fb:29:14:8d:94:f9:1d:cf:6b:0e:a4:35:1d:83:44:2f"
- ),
- new SimpleImmutableEntry<>(
- BuiltinDigests.sha256,
- "SHA256:grxw4KhY1cK6eOczBWs7tDVvo9V0PQw4E1wN1gJvHlw"
- )
- )
- ),
- new SimpleImmutableEntry<>(
- // CHECKSTYLE:OFF
- "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBFImZtcTj842stlcVHLFBFxTEx7lu3jW9aZCvd0r9fUNKZ6LbRPh6l1oJ4ozArnw7XreQBUc5oNd9HB5RNJ8jl1nWXY5cXBA7McZrKZrYmk+zxNhH6UL+kMLaJkyngJHQw== root@osv-linux",
- // CHECKSTYLE:ON
- Arrays.asList(
- new SimpleImmutableEntry<>(
- BuiltinDigests.md5,
- "MD5:e6:dc:a2:4f:5b:11:b2:3c:0f:e8:f6:d8:d1:01:e9:d3"
- ),
- new SimpleImmutableEntry<>(
- BuiltinDigests.sha512,
- "SHA512:4w6ZB78tmFWhpN2J50Ok6WeMJhZp1X0xN0EKWxZmRLcYDbCWhyJDe8lgrQKWqdTCMZ5aNEBl9xQUklcC5Gt2jg"
- )
- )
- )
- ));
-
- List<Object[]> ret = new ArrayList<>();
- for (Map.Entry<String, ? extends Collection<? extends Map.Entry<DigestFactory, String>>> kentry : keyEntries) {
- String keyValue = kentry.getKey();
- try {
- PublicKey key = PublicKeyEntry.parsePublicKeyEntry(keyValue).resolvePublicKey(PublicKeyEntryResolver.FAILING);
- for (Map.Entry<DigestFactory, String> dentry : kentry.getValue()) {
- DigestFactory factory = dentry.getKey();
- String fingerprint = dentry.getValue();
- if (!factory.isSupported()) {
- System.out.println("Skip unsupported digest: " + fingerprint);
- continue;
- }
-
- ret.add(new Object[]{key, factory, fingerprint});
- }
- } catch (InvalidKeySpecException e) {
- System.out.println("Skip unsupported key: " + keyValue);
- }
- }
-
- return ret;
- }
-
- @Test
- public void testFingerprint() throws Exception {
- String name = digestFactory.getName();
- assertEquals(
- String.format("Fingerprint does not match for digest %s", name),
- expected,
- KeyUtils.getFingerPrint(digestFactory, key)
- );
- assertEquals(
- String.format("Fingerprint check failed for digest %s", name),
- new SimpleImmutableEntry<>(true, expected),
- KeyUtils.checkFingerPrint(expected, digestFactory, key)
- );
- assertEquals(
- String.format("Fingerprint check succeeded for invalid digest %s", name),
- new SimpleImmutableEntry<>(false, expected),
- KeyUtils.checkFingerPrint(expected + "A", digestFactory, key)
- );
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java
deleted file mode 100644
index 0f7e07c..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/KeyUtilsTest.java
+++ /dev/null
@@ -1,157 +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.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.attribute.PosixFilePermission;
-import java.security.DigestException;
-import java.util.Collection;
-import java.util.Date;
-import java.util.Map;
-
-import org.apache.sshd.common.digest.BaseDigest;
-import org.apache.sshd.common.digest.BuiltinDigests;
-import org.apache.sshd.common.digest.Digest;
-import org.apache.sshd.common.digest.DigestFactory;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.OsUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class KeyUtilsTest extends BaseTestSupport {
- public KeyUtilsTest() {
- super();
- }
-
- @Test
- public void testGenerateFingerPrintOnException() {
- for (DigestFactory info : BuiltinDigests.VALUES) {
- if (!info.isSupported()) {
- System.out.println("Skip unsupported digest: " + info.getAlgorithm());
- continue;
- }
-
- Exception thrown = new DigestException(info.getAlgorithm() + ":" + info.getBlockSize());
- Digest digest = new BaseDigest(info.getAlgorithm(), info.getBlockSize()) {
- @Override
- public byte[] digest() throws Exception {
- throw thrown;
- }
- };
- String actual = KeyUtils.getFingerPrint(new DigestFactory() {
- @Override
- public String getName() {
- return getCurrentTestName();
- }
-
- @Override
- public String getAlgorithm() {
- return digest.getAlgorithm();
- }
-
- @Override
- public boolean isSupported() {
- return info.isSupported();
- }
-
- @Override
- public int getBlockSize() {
- return info.getBlockSize();
- }
-
- @Override
- public Digest create() {
- return digest;
- }
- }, getCurrentTestName());
- String expected = thrown.getClass().getSimpleName();
- assertEquals("Mismatched fingerprint for " + thrown.getMessage(), expected, actual);
- }
- }
-
- @Test
- public void testGenerateDefaultFingerprintDigest() {
- DigestFactory defaultValue = KeyUtils.getDefaultFingerPrintFactory();
- assertNotNull("No current default fingerprint digest factory", defaultValue);
- try {
- for (DigestFactory f : BuiltinDigests.VALUES) {
- if (!f.isSupported()) {
- System.out.println("Skip unsupported digest=" + f.getAlgorithm());
- continue;
- }
-
- KeyUtils.setDefaultFingerPrintFactory(f);
-
- String data = getClass().getName() + "#" + getCurrentTestName() + "(" + f.getName() + ")";
- String expected = KeyUtils.getFingerPrint(f, data);
- String actual = KeyUtils.getFingerPrint(data);
- assertEquals("Mismatched fingerprint for digest=" + f.getName(), expected, actual);
- }
- } finally {
- KeyUtils.setDefaultFingerPrintFactory(defaultValue); // restore the original
- }
- }
-
- @Test // see SSHD-606
- public void testValidateStrictKeyFilePermissions() throws IOException {
- Path file = getTempTargetRelativeFile(getClass().getSimpleName(), getCurrentTestName());
- outputDebugMessage("%s deletion result=%s", file, Files.deleteIfExists(file));
- assertNull("Unexpected violation for non-existent file: " + file, KeyUtils.validateStrictKeyFilePermissions(file));
-
- assertHierarchyTargetFolderExists(file.getParent());
- try (OutputStream output = Files.newOutputStream(file)) {
- output.write((getClass().getName() + "#" + getCurrentTestName() + "@" + new Date(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
- }
-
- Collection<PosixFilePermission> perms = IoUtils.getPermissions(file);
- if (GenericUtils.isEmpty(perms)) {
- assertNull("Unexpected violation for no permissions file: " + file, KeyUtils.validateStrictKeyFilePermissions(file));
- } else if (OsUtils.isUNIX()) {
- Map.Entry<String, Object> violation = null;
- for (PosixFilePermission p : KeyUtils.STRICTLY_PROHIBITED_FILE_PERMISSION) {
- if (perms.contains(p)) {
- violation = KeyUtils.validateStrictKeyFilePermissions(file);
- assertNotNull("Unexpected success for permission=" + p + " of file " + file + " permissions=" + perms, violation);
- break;
- }
- }
-
- if (violation == null) { // we expect a failure since the parent does not have the necessary permissions
- assertNotNull("Unexpected UNIX success for file " + file + " permissions=" + perms, KeyUtils.validateStrictKeyFilePermissions(file));
- }
- } else {
- assertNull("Unexpected Windows violation for file " + file + " permissions=" + perms, KeyUtils.validateStrictKeyFilePermissions(file));
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/PublicKeyEntryTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/PublicKeyEntryTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/PublicKeyEntryTest.java
deleted file mode 100644
index eee2d72..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/PublicKeyEntryTest.java
+++ /dev/null
@@ -1,62 +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.GeneralSecurityException;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class PublicKeyEntryTest extends BaseTestSupport {
- public PublicKeyEntryTest() {
- super();
- }
-
- @Test
- public void testFallbackResolver() throws Exception {
- PublicKeyEntry entry = // TODO use some
- PublicKeyEntry.parsePublicKeyEntry(
- GenericUtils.join(
- Arrays.asList(getCurrentTestName(), "AAAA", getClass().getSimpleName()), ' '));
- for (PublicKeyEntryResolver resolver : new PublicKeyEntryResolver[]{
- null, PublicKeyEntryResolver.FAILING, PublicKeyEntryResolver.IGNORING}) {
- try {
- PublicKey key = entry.resolvePublicKey(resolver);
- assertSame("Mismatched successful resolver", PublicKeyEntryResolver.IGNORING, resolver);
- assertNull("Unexpected success for resolver=" + resolver + ": " + KeyUtils.getFingerPrint(key), key);
- } catch (GeneralSecurityException e) {
- assertObjectInstanceOf("Mismatched thrown exception for resolver=" + resolver, InvalidKeySpecException.class, e);
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java
deleted file mode 100644
index cc3c379..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java
+++ /dev/null
@@ -1,79 +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.loader;
-
-import java.security.GeneralSecurityException;
-import java.security.Key;
-import java.util.List;
-import java.util.Random;
-
-import javax.crypto.Cipher;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class AESPrivateKeyObfuscatorTest extends BaseTestSupport {
- private static final Random RANDOMIZER = new Random(System.currentTimeMillis());
-
- private final int keyLength;
-
- public AESPrivateKeyObfuscatorTest(int keyLength) {
- this.keyLength = keyLength;
- }
-
- @Parameters(name = "keyLength={0}")
- public static List<Object[]> parameters() {
- List<Integer> lengths = AESPrivateKeyObfuscator.getAvailableKeyLengths();
- assertFalse("No lengths available", GenericUtils.isEmpty(lengths));
- return parameterize(lengths);
- }
-
- @Test
- public void testAvailableKeyLengthExists() throws GeneralSecurityException {
- assertEquals("Not a BYTE size multiple", 0, keyLength % Byte.SIZE);
-
- byte[] iv = new byte[keyLength / Byte.SIZE];
- synchronized (RANDOMIZER) {
- RANDOMIZER.nextBytes(iv);
- }
-
- Key key = new SecretKeySpec(iv, AESPrivateKeyObfuscator.CIPHER_NAME);
- Cipher c = SecurityUtils.getCipher(AESPrivateKeyObfuscator.CIPHER_NAME);
- c.init(Cipher.DECRYPT_MODE, key);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java
deleted file mode 100644
index 482037d..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest.java
+++ /dev/null
@@ -1,108 +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.loader.openssh;
-
-import java.net.URL;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
-import org.apache.sshd.common.config.keys.BuiltinIdentities;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class OpenSSHKeyPairResourceParserTest extends BaseTestSupport {
- private static final OpenSSHKeyPairResourceParser PARSER = OpenSSHKeyPairResourceParser.INSTANCE;
- private final BuiltinIdentities identity;
-
- public OpenSSHKeyPairResourceParserTest(BuiltinIdentities identity) {
- this.identity = identity;
- }
-
- @Parameters(name = "type={0}")
- public static List<Object[]> parameters() {
- return parameterize(BuiltinIdentities.VALUES);
- }
-
- @Test
- public void testLoadKeyPairs() throws Exception {
- Assume.assumeTrue(identity + " not supported", identity.isSupported());
-
- String resourceKey = getClass().getSimpleName() + "-" + identity.getName().toUpperCase() + "-" + KeyPair.class.getSimpleName();
- URL urlKeyPair = getClass().getResource(resourceKey);
- assertNotNull("Missing key-pair resource: " + resourceKey, urlKeyPair);
-
- Collection<KeyPair> pairs = PARSER.loadKeyPairs(urlKeyPair, FilePasswordProvider.EMPTY);
- assertEquals("Mismatched pairs count", 1, GenericUtils.size(pairs));
-
- URL urlPubKey = getClass().getResource(resourceKey + ".pub");
- assertNotNull("Missing public key resource: " + resourceKey, urlPubKey);
-
- List<AuthorizedKeyEntry> entries = AuthorizedKeyEntry.readAuthorizedKeys(urlPubKey);
- assertEquals("Mismatched public keys count", 1, GenericUtils.size(entries));
-
- AuthorizedKeyEntry entry = entries.get(0);
- PublicKey pubEntry = entry.resolvePublicKey(PublicKeyEntryResolver.FAILING);
- assertNotNull("Cannot retrieve public key", pubEntry);
-
- Class<? extends PublicKey> pubType = identity.getPublicKeyType();
- Class<? extends PrivateKey> prvType = identity.getPrivateKeyType();
- for (KeyPair kp : pairs) {
- PublicKey pubKey = ValidateUtils.checkInstanceOf(kp.getPublic(), pubType, "Mismatched public key type");
- assertKeyEquals("Mismatched identity public key", pubEntry, pubKey);
-
- PrivateKey prvKey = ValidateUtils.checkInstanceOf(kp.getPrivate(), prvType, "Mismatched private key type");
- @SuppressWarnings("rawtypes")
- PrivateKeyEntryDecoder decoder =
- OpenSSHKeyPairResourceParser.getPrivateKeyEntryDecoder(prvKey);
- assertNotNull("No private key decoder", decoder);
-
- if (decoder.isPublicKeyRecoverySupported()) {
- @SuppressWarnings("unchecked")
- PublicKey recKey = decoder.recoverPublicKey(prvKey);
- assertKeyEquals("Mismatched recovered public key", pubKey, recKey);
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
deleted file mode 100644
index 69e6b66..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParserTest.java
+++ /dev/null
@@ -1,107 +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.loader.pem;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.PrivateKey;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.commons.ssl.PEMItem;
-import org.apache.commons.ssl.PEMUtil;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class PKCS8PEMResourceKeyPairParserTest extends BaseTestSupport {
- private final String algorithm;
- private final int keySize;
-
- public PKCS8PEMResourceKeyPairParserTest(String algorithm, int keySize) {
- this.algorithm = algorithm;
- this.keySize = keySize;
- }
-
- @Parameters(name = "{0} / {1}")
- public static List<Object[]> parameters() {
- List<Object[]> params = new ArrayList<>();
- for (Integer ks : RSA_SIZES) {
- params.add(new Object[]{KeyUtils.RSA_ALGORITHM, ks});
- }
- for (Integer ks : DSS_SIZES) {
- params.add(new Object[]{KeyUtils.DSS_ALGORITHM, ks});
- }
- return params;
- }
-
- @Test // see SSHD-760
- public void testPkcs8() throws IOException, GeneralSecurityException {
- KeyPairGenerator generator = SecurityUtils.getKeyPairGenerator(algorithm);
- if (keySize > 0) {
- generator.initialize(keySize);
- }
- KeyPair kp = generator.generateKeyPair();
-
- try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
- Collection<Object> items = new ArrayList<>();
- PrivateKey prv1 = kp.getPrivate();
- items.add(new PEMItem(prv1.getEncoded(), "PRIVATE KEY"));
- byte[] bytes = PEMUtil.encode(items);
- os.write(bytes);
- os.close();
-
- try (ByteArrayInputStream bais = new ByteArrayInputStream(os.toByteArray())) {
- KeyPair kp2 = SecurityUtils.loadKeyPairIdentity(getCurrentTestName(), bais, null);
-
- assertEquals("Mismatched public key", kp.getPublic(), kp2.getPublic());
- assertEquals("Mismatched private key", prv1, kp2.getPrivate());
- }
- }
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + "[" + algorithm + "/" + keySize + "]";
- }
-}
[05/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java b/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java
index ba58c5e..589ab2f 100644
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java
+++ b/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java
@@ -18,76 +18,23 @@
*/
package org.apache.sshd.util.test;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.security.Key;
-import java.security.KeyPair;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.interfaces.ECKey;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.ECField;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.EllipticCurve;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.TimeUnit;
-
import org.apache.sshd.client.SshClient;
import org.apache.sshd.common.io.DefaultIoServiceFactoryFactory;
import org.apache.sshd.common.io.IoServiceFactoryFactory;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.IoUtils;
import org.apache.sshd.common.util.net.SshdSocketAddress;
import org.apache.sshd.server.SshServer;
-import org.junit.Assert;
import org.junit.Rule;
-import org.junit.rules.TestName;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
-import org.junit.runner.RunWith;
/**
* Helper used as base class for all test classes
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-@RunWith(JUnit4SingleInstanceClassRunner.class)
-public abstract class BaseTestSupport extends Assert {
- public static final String TEMP_SUBFOLDER_NAME = "temp";
+public abstract class BaseTestSupport extends JUnitTestSupport {
// can be used to override the 'localhost' with an address other than 127.0.0.1 in case it is required
public static final String TEST_LOCALHOST = System.getProperty("org.apache.sshd.test.localhost", SshdSocketAddress.LOCALHOST_IPV4);
- public static final boolean OUTPUT_DEBUG_MESSAGES = Boolean.parseBoolean(System.getProperty("org.apache.sshd.test.outputDebugMessages", "false"));
- public static final String MAIN_SUBFOLDER = "main";
- public static final String TEST_SUBFOLDER = "test";
- public static final String RESOURCES_SUBFOLDER = "resources";
-
- // useful test sizes for keys
- public static final List<Integer> DSS_SIZES =
- Collections.unmodifiableList(Arrays.asList(512, 768, 1024));
- public static final List<Integer> RSA_SIZES =
- Collections.unmodifiableList(Arrays.asList(1024, 2048, 3072, 4096));
- public static final List<Integer> ED25519_SIZES =
- Collections.unmodifiableList(Arrays.asList(256));
@Rule
public final TestWatcher rule = new TestWatcher() {
@@ -115,503 +62,21 @@ public abstract class BaseTestSupport extends Assert {
}
};
- @Rule
- public final TestName testNameHolder = new TestName();
- private Path targetFolder;
- private Path tempFolder;
-
protected BaseTestSupport() {
super();
}
- public final String getCurrentTestName() {
- return testNameHolder.getMethodName();
- }
-
protected SshServer setupTestServer() {
- return Utils.setupTestServer(getClass());
+ return CoreTestSupportUtils.setupTestServer(getClass());
}
protected SshClient setupTestClient() {
- return Utils.setupTestClient(getClass());
- }
-
- protected KeyPairProvider createTestHostKeyProvider() {
- return Utils.createTestHostKeyProvider(getClass());
- }
-
- /**
- * Attempts to build a <U>relative</U> path whose root is the location
- * of the TEMP sub-folder of the Maven "target" folder associated
- * with the project
- *
- * @param comps The path components - ignored if {@code null}/empty
- * @return The {@link Path} representing the result - same as target folder if no components
- * @see #TEMP_SUBFOLDER_NAME
- * @see #getTargetRelativeFile(Collection)
- */
- protected Path getTempTargetRelativeFile(String... comps) {
- return getTempTargetRelativeFile(GenericUtils.isEmpty(comps) ? Collections.emptyList() : Arrays.asList(comps));
- }
-
- /**
- * Attempts to build a <U>relative</U> path whose root is the location
- * of the TEMP sub-folder of the Maven "target" folder associated
- * with the project
- *
- * @param comps The path components - ignored if {@code null}/empty
- * @return The {@link Path} representing the result - same as target folder if no components
- * @see #TEMP_SUBFOLDER_NAME
- * @see #getTempTargetFolder()
- */
- protected Path getTempTargetRelativeFile(Collection<String> comps) {
- return Utils.resolve(getTempTargetFolder(), comps);
- }
-
- /**
- * @return The TEMP sub-folder {@link Path} of the Maven "target" folder
- * associated with the project - never {@code null}
- */
- protected Path getTempTargetFolder() {
- synchronized (TEMP_SUBFOLDER_NAME) {
- if (tempFolder == null) {
- tempFolder = Objects.requireNonNull(detectTargetFolder(), "No target folder detected").resolve(TEMP_SUBFOLDER_NAME);
- }
- }
-
- return tempFolder;
- }
-
- /**
- * Attempts to build a <U>relative</U> path whose root is the location
- * of the Maven "target" folder associated with the project
- *
- * @param comps The path components - ignored if {@code null}/empty
- * @return The {@link Path} representing the result - same as target folder if no components
- */
- protected Path getTargetRelativeFile(String... comps) {
- return getTargetRelativeFile(GenericUtils.isEmpty(comps) ? Collections.emptyList() : Arrays.asList(comps));
- }
-
- /**
- * Attempts to build a <U>relative</U> path whose root is the location
- * of the Maven "target" folder associated with the project
- *
- * @param comps The path components - ignored if {@code null}/empty
- * @return The {@link Path} representing the result - same as target folder if no components
- * @see #detectTargetFolder()
- */
- protected Path getTargetRelativeFile(Collection<String> comps) {
- return Utils.resolve(detectTargetFolder(), comps);
- }
-
- /**
- * Attempts to detect the location of the Maven "target" folder
- * associated with the project that contains the actual class extending this
- * base class
- *
- * @return The {@link File} representing the location of the "target" folder
- * @throws IllegalArgumentException If failed to detect the folder
- */
- protected Path detectTargetFolder() throws IllegalArgumentException {
- synchronized (TEMP_SUBFOLDER_NAME) {
- if (targetFolder == null) {
- targetFolder = Objects.requireNonNull(Utils.detectTargetFolder(getClass()), "Failed to detect target folder").toPath();
- }
- }
-
- return targetFolder;
- }
-
- /**
- * Creates a folder bearing the class's simple name under the project's target temporary folder
- *
- * @return The created folder {@link Path}
- * @throws IOException If failed to detect or create the folder's location
- * @see #detectTargetFolder() detectTargetFolder
- * @see #assertHierarchyTargetFolderExists(Path, LinkOption...) assertHierarchyTargetFolderExists
- */
- protected Path createTempClassFolder() throws IOException {
- Path tmpDir = detectTargetFolder();
- return assertHierarchyTargetFolderExists(tmpDir.resolve(getClass().getSimpleName()));
- }
-
- protected Path detectSourcesFolder() throws IllegalStateException {
- Path target = detectTargetFolder();
- Path parent = target.getParent();
- return parent.resolve("src");
- }
-
- protected Path getTestResourcesFolder() {
- try {
- URL url = getClass().getResource(getClass().getSimpleName() + ".class");
- return Paths.get(url.toURI()).getParent();
- } catch (URISyntaxException e) {
- throw new RuntimeException(e);
- }
- }
-
- protected Path getClassResourcesFolder(String resType /* test or main */) {
- return getClassResourcesFolder(resType, getClass());
- }
-
- protected Path getClassResourcesFolder(String resType /* test or main */, Class<?> clazz) {
- return getPackageResourcesFolder(resType, clazz.getPackage());
- }
-
- protected Path getPackageResourcesFolder(String resType /* test or main */, Package pkg) {
- return getPackageResourcesFolder(resType, pkg.getName());
- }
-
- protected Path getPackageResourcesFolder(String resType /* test or main */, String pkgName) {
- Path src = detectSourcesFolder();
- Path root = src.resolve(resType);
- Path resources = root.resolve(RESOURCES_SUBFOLDER);
- return resources.resolve(pkgName.replace('.', File.separatorChar));
- }
-
- /* ------------------- Useful extra test helpers ---------------------- */
-
- public static String shuffleCase(CharSequence cs) {
- if (GenericUtils.isEmpty(cs)) {
- return "";
- }
-
- StringBuilder sb = new StringBuilder(cs.length());
- for (int index = 0; index < cs.length(); index++) {
- char ch = cs.charAt(index);
- double v = Math.random();
- if (Double.compare(v, 0.3d) < 0) {
- ch = Character.toUpperCase(ch);
- } else if ((Double.compare(v, 0.3d) >= 0) && (Double.compare(v, 0.6d) < 0)) {
- ch = Character.toLowerCase(ch);
- }
- sb.append(ch);
- }
-
- return sb.toString();
- }
-
- public static String repeat(CharSequence csq, int nTimes) {
- if (GenericUtils.isEmpty(csq) || (nTimes <= 0)) {
- return "";
- }
-
- StringBuilder sb = new StringBuilder(nTimes * csq.length());
- for (int index = 0; index < nTimes; index++) {
- sb.append(csq);
- }
-
- return sb.toString();
- }
-
- public static List<Object[]> parameterize(Collection<?> params) {
- if (GenericUtils.isEmpty(params)) {
- return Collections.emptyList();
- }
-
- List<Object[]> result = new ArrayList<>(params.size());
- for (Object p : params) {
- result.add(new Object[]{p});
- }
-
- return result;
- }
-
- /* ----------------------- Useful extra assertions --------------------- */
-
- public static void assertEquals(String message, boolean expected, boolean actual) {
- assertEquals(message, Boolean.valueOf(expected), Boolean.valueOf(actual));
- }
-
- public static <T> void assertEquals(String message, Iterable<? extends T> expected, Iterable<? extends T> actual) {
- if (expected != actual) {
- assertEquals(message, expected.iterator(), actual.iterator());
- }
- }
-
- public static <T> void assertEquals(String message, Iterator<? extends T> expected, Iterator<? extends T> actual) {
- if (expected == actual) {
- return;
- }
-
- for (int index = 0; expected.hasNext(); index++) {
- assertTrue(message + "[next actual index=" + index + "]", actual.hasNext());
-
- T expValue = expected.next();
- T actValue = actual.next();
- assertEquals(message + "[iterator index=" + index + "]", expValue, actValue);
- }
-
- // once expected is exhausted make sure no more actual items left
- assertFalse(message + "[non-empty-actual]", actual.hasNext());
- }
-
- public static Path assertHierarchyTargetFolderExists(Path folder, LinkOption... options) throws IOException {
- if (Files.exists(folder, options)) {
- assertTrue("Target is an existing file instead of a folder: " + folder, Files.isDirectory(folder, options));
- } else {
- Files.createDirectories(folder);
- }
-
- return folder;
- }
-
- public static void assertFileContentsEquals(String prefix, Path expected, Path actual) throws IOException {
- long cmpSize = Files.size(expected);
- assertEquals(prefix + ": Mismatched file size", cmpSize, Files.size(expected));
-
- try (InputStream expStream = Files.newInputStream(expected);
- InputStream actStream = Files.newInputStream(actual)) {
- byte[] expData = new byte[IoUtils.DEFAULT_COPY_SIZE];
- byte[] actData = new byte[expData.length];
-
- for (long offset = 0L; offset < cmpSize;) {
- Arrays.fill(expData, (byte) 0);
- int expLen = expStream.read(expData);
- Arrays.fill(actData, (byte) 0);
- int actLen = actStream.read(actData);
- assertEquals(prefix + ": Mismatched read size at offset=" + offset, expLen, actLen);
- assertArrayEquals(prefix + ": Mismatched data at offset=" + offset, expData, actData);
-
- offset += expLen;
- }
- }
- }
-
- public static File assertHierarchyTargetFolderExists(File folder) {
- if (folder.exists()) {
- assertTrue("Target is an existing file instead of a folder: " + folder.getAbsolutePath(), folder.isDirectory());
- } else {
- assertTrue("Failed to create hierarchy of " + folder.getAbsolutePath(), folder.mkdirs());
- }
-
- return folder;
- }
-
- public static void assertObjectInstanceOf(String message, Class<?> expected, Object obj) {
- assertNotNull(message + " - no actual object", obj);
-
- Class<?> actual = obj.getClass();
- if (!expected.isAssignableFrom(actual)) {
- fail(message + " - actual object type (" + actual.getName() + ") incompatible with expected (" + expected.getName() + ")");
- }
- }
-
- public static <E> void assertListEquals(String message, List<? extends E> expected, List<? extends E> actual) {
- int expSize = GenericUtils.size(expected);
- int actSize = GenericUtils.size(actual);
- assertEquals(message + "[size]", expSize, actSize);
-
- for (int index = 0; index < expSize; index++) {
- E expValue = expected.get(index);
- E actValue = actual.get(index);
- assertEquals(message + "[" + index + "]", expValue, actValue);
- }
- }
-
- public static <K, V> void assertMapEquals(String message, Map<? extends K, ? extends V> expected, Map<? super K, ? extends V> actual) {
- int numItems = GenericUtils.size(expected);
- assertEquals(message + "[size]", numItems, GenericUtils.size(actual));
-
- if (numItems > 0) {
- expected.forEach((key, expValue) -> {
- V actValue = actual.get(key);
- assertEquals(message + "[" + key + "]", expValue, actValue);
- });
- }
- }
-
- public static void assertKeyPairEquals(String message, KeyPair expected, KeyPair actual) {
- assertKeyEquals(message + "[public]", expected.getPublic(), actual.getPublic());
- assertKeyEquals(message + "[private]", expected.getPrivate(), actual.getPrivate());
- }
-
- public static <T extends Key> void assertKeyEquals(String message, T expected, T actual) {
- if (expected == actual) {
- return;
- }
-
- assertEquals(message + "[algorithm]", expected.getAlgorithm(), actual.getAlgorithm());
-
- if (expected instanceof RSAPublicKey) {
- assertRSAPublicKeyEquals(message, RSAPublicKey.class.cast(expected), RSAPublicKey.class.cast(actual));
- } else if (expected instanceof DSAPublicKey) {
- assertDSAPublicKeyEquals(message, DSAPublicKey.class.cast(expected), DSAPublicKey.class.cast(actual));
- } else if (expected instanceof ECPublicKey) {
- assertECPublicKeyEquals(message, ECPublicKey.class.cast(expected), ECPublicKey.class.cast(actual));
- } else if (expected instanceof RSAPrivateKey) {
- assertRSAPrivateKeyEquals(message, RSAPrivateKey.class.cast(expected), RSAPrivateKey.class.cast(actual));
- } else if (expected instanceof ECPrivateKey) {
- assertECPrivateKeyEquals(message, ECPrivateKey.class.cast(expected), ECPrivateKey.class.cast(actual));
- }
- assertArrayEquals(message + "[encdoded-data]", expected.getEncoded(), actual.getEncoded());
- }
-
- public static void assertRSAPublicKeyEquals(String message, RSAPublicKey expected, RSAPublicKey actual) {
- if (expected == actual) {
- return;
- }
-
- assertEquals(message + "[e]", expected.getPublicExponent(), actual.getPublicExponent());
- assertEquals(message + "[n]", expected.getModulus(), actual.getModulus());
- }
-
- public static void assertDSAPublicKeyEquals(String message, DSAPublicKey expected, DSAPublicKey actual) {
- if (expected == actual) {
- return;
- }
-
- assertEquals(message + "[y]", expected.getY(), actual.getY());
- assertDSAParamsEquals(message + "[params]", expected.getParams(), actual.getParams());
- }
-
- public static void assertECPublicKeyEquals(String message, ECPublicKey expected, ECPublicKey actual) {
- if (expected == actual) {
- return;
- }
-
- assertECPointEquals(message + "[W]", expected.getW(), actual.getW());
- assertECParameterSpecEquals(message, expected, actual);
- }
-
- public static void assertRSAPrivateKeyEquals(String message, RSAPrivateKey expected, RSAPrivateKey actual) {
- if (expected == actual) {
- return;
- }
-
- assertEquals(message + "[d]", expected.getPrivateExponent(), actual.getPrivateExponent());
- assertEquals(message + "[n]", expected.getModulus(), actual.getModulus());
- }
-
- public static void assertDSAPrivateKeyEquals(String message, DSAPrivateKey expected, DSAPrivateKey actual) {
- if (expected == actual) {
- return;
- }
-
- assertEquals(message + "[x]", expected.getX(), actual.getX());
- assertDSAParamsEquals(message + "[params]", expected.getParams(), actual.getParams());
- }
-
- public static void assertDSAParamsEquals(String message, DSAParams expected, DSAParams actual) {
- if (expected == actual) {
- return;
- }
-
- assertEquals(message + "[g]", expected.getG(), actual.getG());
- assertEquals(message + "[p]", expected.getP(), actual.getP());
- assertEquals(message + "[q]", expected.getQ(), actual.getQ());
- }
-
- public static void assertECPrivateKeyEquals(String message, ECPrivateKey expected, ECPrivateKey actual) {
- if (expected == actual) {
- return;
- }
-
- assertEquals(message + "[S]", expected.getS(), actual.getS());
- assertECParameterSpecEquals(message, expected, actual);
- }
-
- public static void assertECParameterSpecEquals(String message, ECKey expected, ECKey actual) {
- if (expected == actual) {
- return;
- }
- assertECParameterSpecEquals(message, expected.getParams(), actual.getParams());
- }
-
- public static void assertECParameterSpecEquals(String message, ECParameterSpec expected, ECParameterSpec actual) {
- if (expected == actual) {
- return;
- }
-
- assertEquals(message + "[order]", expected.getOrder(), actual.getOrder());
- assertEquals(message + "[cofactor]", expected.getCofactor(), actual.getCofactor());
- assertECPointEquals(message + "[generator]", expected.getGenerator(), actual.getGenerator());
- assertCurveEquals(message + "[curve]", expected.getCurve(), actual.getCurve());
- }
-
- public static void assertCurveEquals(String message, EllipticCurve expected, EllipticCurve actual) {
- if (expected == actual) {
- return;
- }
-
- assertEquals(message + "[A]", expected.getA(), actual.getA());
- assertEquals(message + "[B]", expected.getB(), actual.getB());
- assertArrayEquals(message + "[seed]", expected.getSeed(), actual.getSeed());
- assertECFieldEquals(message + "[field]", expected.getField(), actual.getField());
- }
-
- public static void assertECFieldEquals(String message, ECField expected, ECField actual) {
- if (expected == actual) {
- return;
- }
-
- assertEquals(message + "[size]", expected.getFieldSize(), actual.getFieldSize());
- }
-
- public static void assertECPointEquals(String message, ECPoint expected, ECPoint actual) {
- if (expected == actual) {
- return;
- }
-
- assertEquals(message + "[x]", expected.getAffineX(), actual.getAffineX());
- assertEquals(message + "[y]", expected.getAffineY(), actual.getAffineY());
- }
-
- public static void assertFileLength(File file, long length, long timeout) throws Exception {
- assertFileLength(file.toPath(), length, timeout);
- }
-
- /**
- * Waits the specified timeout for the file to exist and have the required length
- *
- * @param file The file {@link Path} to check
- * @param length Expected length
- * @param timeout Timeout (msec.) to wait for satisfying the requirements
- * @throws Exception If failed to access the file
- */
- public static void assertFileLength(Path file, long length, long timeout) throws Exception {
- if (waitForFile(file, length, timeout)) {
- return;
- }
- assertTrue("File not found: " + file, Files.exists(file));
- assertEquals("Mismatched file size for " + file, length, Files.size(file));
- }
-
- public static boolean waitForFile(Path file, long length, long timeout) throws Exception {
- while (timeout > 0L) {
- long sleepTime = Math.min(timeout, 100L);
- if (Files.exists(file) && (Files.size(file) == length)) {
- return true;
- }
-
- long sleepStart = System.nanoTime();
- Thread.sleep(sleepTime);
- long sleepEnd = System.nanoTime();
- long nanoSleep = sleepEnd - sleepStart;
-
- sleepTime = TimeUnit.NANOSECONDS.toMillis(nanoSleep);
- timeout -= sleepTime;
- }
-
- return false;
- }
-
- public static void outputDebugMessage(String format, Object... args) {
- if (OUTPUT_DEBUG_MESSAGES) {
- outputDebugMessage(String.format(format, args));
- }
- }
-
- public static void outputDebugMessage(Object message) {
- if (OUTPUT_DEBUG_MESSAGES) {
- System.out.append("===[DEBUG]=== ").println(message);
- }
+ return CoreTestSupportUtils.setupTestClient(getClass());
}
public static IoServiceFactoryFactory getIoServiceProvider() {
DefaultIoServiceFactoryFactory factory =
- DefaultIoServiceFactoryFactory.getDefaultIoServiceFactoryFactoryInstance();
+ DefaultIoServiceFactoryFactory.getDefaultIoServiceFactoryFactoryInstance();
return factory.getIoServiceProvider();
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/CoreTestSupportUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/CoreTestSupportUtils.java b/sshd-core/src/test/java/org/apache/sshd/util/test/CoreTestSupportUtils.java
new file mode 100644
index 0000000..e6bb6cb
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/util/test/CoreTestSupportUtils.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.util.test;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+
+import org.apache.sshd.client.SshClient;
+import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
+import org.apache.sshd.client.keyverifier.AcceptAllServerKeyVerifier;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.auth.pubkey.AcceptAllPublickeyAuthenticator;
+import org.apache.sshd.server.shell.UnknownCommandFactory;
+
+public final class CoreTestSupportUtils {
+ private CoreTestSupportUtils() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ public static int getFreePort() throws Exception {
+ try (ServerSocket s = new ServerSocket()) {
+ s.setReuseAddress(true);
+ s.bind(new InetSocketAddress((InetAddress) null, 0));
+ return s.getLocalPort();
+ }
+ }
+
+ public static SshClient setupTestClient(Class<?> anchor) {
+ SshClient client = SshClient.setUpDefaultClient();
+ client.setServerKeyVerifier(AcceptAllServerKeyVerifier.INSTANCE);
+ client.setHostConfigEntryResolver(HostConfigEntryResolver.EMPTY);
+ client.setKeyPairProvider(KeyPairProvider.EMPTY_KEYPAIR_PROVIDER);
+ return client;
+ }
+
+ public static SshServer setupTestServer(Class<?> anchor) {
+ SshServer sshd = SshServer.setUpDefaultServer();
+ sshd.setKeyPairProvider(CommonTestSupportUtils.createTestHostKeyProvider(anchor));
+ sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
+ sshd.setPublickeyAuthenticator(AcceptAllPublickeyAuthenticator.INSTANCE);
+ sshd.setShellFactory(EchoShellFactory.INSTANCE);
+ sshd.setCommandFactory(UnknownCommandFactory.INSTANCE);
+ return sshd;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParameters.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParameters.java b/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParameters.java
deleted file mode 100644
index acf44a5..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParameters.java
+++ /dev/null
@@ -1,48 +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.util.test;
-
-import org.junit.runners.model.InitializationError;
-import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters;
-import org.junit.runners.parameterized.TestWithParameters;
-
-/**
- * Uses a cached created instance instead of a new one on every call of {@code #createTest()}
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class JUnit4ClassRunnerWithParameters extends BlockJUnit4ClassRunnerWithParameters {
- private volatile Object testInstance;
-
- public JUnit4ClassRunnerWithParameters(TestWithParameters test) throws InitializationError {
- super(test);
- }
-
- @Override
- public Object createTest() throws Exception {
- synchronized (this) {
- if (testInstance == null) {
- testInstance = super.createTest();
- }
- }
-
- return testInstance;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParametersFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParametersFactory.java b/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParametersFactory.java
deleted file mode 100644
index 12ab252..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4ClassRunnerWithParametersFactory.java
+++ /dev/null
@@ -1,58 +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.util.test;
-
-import org.junit.runner.Runner;
-import org.junit.runners.model.InitializationError;
-import org.junit.runners.parameterized.ParametersRunnerFactory;
-import org.junit.runners.parameterized.TestWithParameters;
-
-/**
- * Avoids re-creating a test class instance for each parameterized test method. Usage:
- *
- * <PRE><code>
- * @FixMethodOrder(MethodSorters.NAME_ASCENDING)
- * @RunWith(Parameterized.class)
- * @UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
- * public class MyParameterizedTest {
- * public MyParameterizedTest(...params...) {
- * ....
- * }
- *
- * @Parameters(...)
- * public static List<Object[]> parameters() {
- * ...
- * }
- * }
- * </code></PRE>
- *
- * @see JUnit4ClassRunnerWithParameters
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class JUnit4ClassRunnerWithParametersFactory implements ParametersRunnerFactory {
- public JUnit4ClassRunnerWithParametersFactory() {
- super();
- }
-
- @Override
- public Runner createRunnerForTestWithParameters(TestWithParameters test) throws InitializationError {
- return new JUnit4ClassRunnerWithParameters(test);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4SingleInstanceClassRunner.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4SingleInstanceClassRunner.java b/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4SingleInstanceClassRunner.java
deleted file mode 100644
index a8f11ce..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/JUnit4SingleInstanceClassRunner.java
+++ /dev/null
@@ -1,54 +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.util.test;
-
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.junit.runners.BlockJUnit4ClassRunner;
-import org.junit.runners.model.InitializationError;
-import org.junit.runners.model.TestClass;
-
-/**
- * @see <A HREF="https://issues.apache.org/jira/browse/SSHD-764">SSHD-764</A>
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class JUnit4SingleInstanceClassRunner extends BlockJUnit4ClassRunner {
- private final AtomicReference<Map.Entry<Class<?>, ?>> testHolder = new AtomicReference<>();
-
- public JUnit4SingleInstanceClassRunner(Class<?> klass) throws InitializationError {
- super(klass);
- }
-
- @Override
- protected Object createTest() throws Exception {
- Map.Entry<Class<?>, ?> lastTest = testHolder.get();
- Class<?> lastTestClass = (lastTest == null) ? null : lastTest.getKey();
- TestClass curTest = getTestClass();
- Class<?> curTestClass = curTest.getJavaClass();
- if (curTestClass == lastTestClass) {
- return lastTest.getValue();
- }
-
- Object instance = super.createTest();
- testHolder.set(new SimpleImmutableEntry<>(curTestClass, instance));
- return instance;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/NoIoTestCase.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/NoIoTestCase.java b/sshd-core/src/test/java/org/apache/sshd/util/test/NoIoTestCase.java
deleted file mode 100644
index 7d52892..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/NoIoTestCase.java
+++ /dev/null
@@ -1,30 +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.util.test;
-
-/**
- * Marker interface used as <A HREF="https://github.com/junit-team/junit4/wiki/categories">jUnit category</A>
- * to indicate a test that does not require real client/server interaction.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface NoIoTestCase {
- // Marker interface
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java b/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
deleted file mode 100644
index b4eb84c..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
+++ /dev/null
@@ -1,56 +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.util.test;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class OutputCountTrackingOutputStream extends FilterOutputStream {
- protected long writeCount;
-
- public OutputCountTrackingOutputStream(OutputStream out) {
- super(out);
- }
-
- @Override
- public void write(int b) throws IOException {
- out.write(b);
- updateWriteCount(1L);
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- out.write(b, off, len); // don't call super since it calls the single 'write'
- updateWriteCount(len);
- }
-
- public long getWriteCount() {
- return writeCount;
- }
-
- protected long updateWriteCount(long delta) {
- writeCount += delta;
- return writeCount;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/TeeOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/TeeOutputStream.java b/sshd-core/src/test/java/org/apache/sshd/util/test/TeeOutputStream.java
deleted file mode 100644
index 4cd6014..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/TeeOutputStream.java
+++ /dev/null
@@ -1,64 +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.util.test;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class TeeOutputStream extends OutputStream {
-
- private OutputStream[] tees;
-
- public TeeOutputStream(OutputStream... tees) {
- this.tees = tees;
- }
-
- @Override
- public void write(int b) throws IOException {
- for (OutputStream s : tees) {
- s.write(b);
- }
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- for (OutputStream s : tees) {
- s.write(b, off, len);
- }
- }
-
- @Override
- public void flush() throws IOException {
- for (OutputStream s : tees) {
- s.flush();
- }
- }
-
- @Override
- public void close() throws IOException {
- for (OutputStream s : tees) {
- s.close();
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/util/test/Utils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/Utils.java b/sshd-core/src/test/java/org/apache/sshd/util/test/Utils.java
deleted file mode 100644
index a007239..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/Utils.java
+++ /dev/null
@@ -1,652 +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.util.test;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.MalformedURLException;
-import java.net.ServerSocket;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.security.CodeSource;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.ProtectionDomain;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.client.SshClient;
-import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
-import org.apache.sshd.client.keyverifier.AcceptAllServerKeyVerifier;
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
-import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.keyprovider.KeyPairProviderHolder;
-import org.apache.sshd.common.random.Random;
-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.security.SecurityUtils;
-import org.apache.sshd.server.SshServer;
-import org.apache.sshd.server.auth.pubkey.AcceptAllPublickeyAuthenticator;
-import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
-import org.apache.sshd.server.shell.UnknownCommandFactory;
-
-public final class Utils {
- /**
- * URL/URI scheme that refers to a file
- */
- public static final String FILE_URL_SCHEME = "file";
- /**
- * Prefix used in URL(s) that reference a file resource
- */
- public static final String FILE_URL_PREFIX = FILE_URL_SCHEME + ":";
-
- /**
- * Separator used in URL(s) that reference a resource inside a JAR
- * to denote the sub-path inside the JAR
- */
- public static final char RESOURCE_SUBPATH_SEPARATOR = '!';
-
- /**
- * Suffix of JAR files
- */
- public static final String JAR_FILE_SUFFIX = ".jar";
-
- /**
- * URL/URI scheme that refers to a JAR
- */
- public static final String JAR_URL_SCHEME = "jar";
-
- /**
- * Prefix used in URL(s) that reference a resource inside a JAR
- */
- public static final String JAR_URL_PREFIX = JAR_URL_SCHEME + ":";
-
- /**
- * Suffix of compile Java class files
- */
- public static final String CLASS_FILE_SUFFIX = ".class";
-
- public static final List<String> TARGET_FOLDER_NAMES = // NOTE: order is important
- Collections.unmodifiableList(
- Arrays.asList("target" /* Maven */, "build" /* Gradle */));
-
- public static final String DEFAULT_TEST_HOST_KEY_PROVIDER_ALGORITHM = KeyUtils.RSA_ALGORITHM;
- // uses a cached instance to avoid re-creating the keys as it is a time-consuming effort
- private static final AtomicReference<KeyPairProvider> KEYPAIR_PROVIDER_HOLDER = new AtomicReference<>();
- // uses a cached instance to avoid re-creating the keys as it is a time-consuming effort
- private static final Map<String, FileKeyPairProvider> PROVIDERS_MAP = new ConcurrentHashMap<>();
-
- private Utils() {
- throw new UnsupportedOperationException("No instance");
- }
-
- public static KeyPairProvider createTestHostKeyProvider(Class<?> anchor) {
- KeyPairProvider provider = KEYPAIR_PROVIDER_HOLDER.get();
- if (provider != null) {
- return provider;
- }
-
- File targetFolder = Objects.requireNonNull(detectTargetFolder(anchor), "Failed to detect target folder");
- File file = new File(targetFolder, "hostkey." + DEFAULT_TEST_HOST_KEY_PROVIDER_ALGORITHM.toLowerCase());
- provider = createTestHostKeyProvider(file);
-
- KeyPairProvider prev = KEYPAIR_PROVIDER_HOLDER.getAndSet(provider);
- if (prev != null) { // check if somebody else beat us to it
- return prev;
- } else {
- return provider;
- }
- }
-
- public static KeyPairProvider createTestHostKeyProvider(File file) {
- return createTestHostKeyProvider(Objects.requireNonNull(file, "No file").toPath());
- }
-
- public static KeyPairProvider createTestHostKeyProvider(Path path) {
- SimpleGeneratorHostKeyProvider keyProvider = new SimpleGeneratorHostKeyProvider();
- keyProvider.setPath(Objects.requireNonNull(path, "No path"));
- keyProvider.setAlgorithm(DEFAULT_TEST_HOST_KEY_PROVIDER_ALGORITHM);
- return validateKeyPairProvider(keyProvider);
- }
-
- public static KeyPair getFirstKeyPair(KeyPairProviderHolder holder) {
- return getFirstKeyPair(Objects.requireNonNull(holder, "No holder").getKeyPairProvider());
- }
-
- public static KeyPair getFirstKeyPair(KeyIdentityProvider provider) {
- Objects.requireNonNull(provider, "No key pair provider");
- Iterable<? extends KeyPair> pairs = Objects.requireNonNull(provider.loadKeys(), "No loaded keys");
- Iterator<? extends KeyPair> iter = Objects.requireNonNull(pairs.iterator(), "No keys iterator");
- ValidateUtils.checkTrue(iter.hasNext(), "Empty loaded kyes iterator");
- return Objects.requireNonNull(iter.next(), "No key pair in iterator");
- }
-
- public static KeyPair generateKeyPair(String algorithm, int keySize) throws GeneralSecurityException {
- KeyPairGenerator gen = SecurityUtils.getKeyPairGenerator(algorithm);
- if (KeyUtils.EC_ALGORITHM.equalsIgnoreCase(algorithm)) {
- ECCurves curve = ECCurves.fromCurveSize(keySize);
- if (curve == null) {
- throw new InvalidKeySpecException("Unknown curve for key size=" + keySize);
- }
- gen.initialize(curve.getParameters());
- } else {
- gen.initialize(keySize);
- }
-
- return gen.generateKeyPair();
- }
-
- public static FileKeyPairProvider createTestKeyPairProvider(String resource) {
- File file = getFile(resource);
- String filePath = file.getAbsolutePath();
- FileKeyPairProvider provider = PROVIDERS_MAP.get(filePath);
- if (provider != null) {
- return provider;
- }
-
- provider = new FileKeyPairProvider();
- provider.setFiles(Collections.singletonList(file));
- provider = validateKeyPairProvider(provider);
-
- FileKeyPairProvider prev = PROVIDERS_MAP.put(filePath, provider);
- if (prev != null) { // check if somebody else beat us to it
- return prev;
- } else {
- return provider;
- }
- }
-
- private static <P extends KeyIdentityProvider> P validateKeyPairProvider(P provider) {
- Objects.requireNonNull(provider, "No provider");
-
- // get the I/O out of the way
- Iterable<KeyPair> keys = Objects.requireNonNull(provider.loadKeys(), "No keys loaded");
- if (keys instanceof Collection<?>) {
- ValidateUtils.checkNotNullAndNotEmpty((Collection<?>) keys, "Empty keys loaded");
- }
-
- return provider;
- }
-
- public static Random getRandomizerInstance() {
- Factory<Random> factory = SecurityUtils.getRandomFactory();
- return factory.create();
- }
-
- public static int getFreePort() throws Exception {
- try (ServerSocket s = new ServerSocket()) {
- s.setReuseAddress(true);
- s.bind(new InetSocketAddress((InetAddress) null, 0));
- return s.getLocalPort();
- }
- }
-
- private static File getFile(String resource) {
- URL url = Utils.class.getClassLoader().getResource(resource);
- try {
- return new File(url.toURI());
- } catch (URISyntaxException e) {
- return new File(url.getPath());
- }
- }
-
- public static Path resolve(Path root, String... children) {
- if (GenericUtils.isEmpty(children)) {
- return root;
- } else {
- return resolve(root, Arrays.asList(children));
- }
- }
-
- public static Path resolve(Path root, Collection<String> children) {
- Path path = root;
- if (!GenericUtils.isEmpty(children)) {
- for (String child : children) {
- path = path.resolve(child);
- }
- }
-
- return path;
- }
-
- public static File resolve(File root, String... children) {
- if (GenericUtils.isEmpty(children)) {
- return root;
- } else {
- return resolve(root, Arrays.asList(children));
- }
- }
-
- public static File resolve(File root, Collection<String> children) {
- File path = root;
- if (!GenericUtils.isEmpty(children)) {
- for (String child : children) {
- path = new File(path, child);
- }
- }
-
- return path;
- }
-
- /**
- * Removes the specified file - if it is a directory, then its children
- * are deleted recursively and then the directory itself. <B>Note:</B>
- * no attempt is made to make sure that {@link File#delete()} was successful
- *
- * @param file The {@link File} to be deleted - ignored if {@code null}
- * or does not exist anymore
- * @return The <tt>file</tt> argument
- */
- public static File deleteRecursive(File file) {
- if ((file == null) || (!file.exists())) {
- return file;
- }
-
- if (file.isDirectory()) {
- File[] children = file.listFiles();
- if (!GenericUtils.isEmpty(children)) {
- for (File child : children) {
- deleteRecursive(child);
- }
- }
- }
-
- // seems that if a file is not writable it cannot be deleted
- if (!file.canWrite()) {
- file.setWritable(true, false);
- }
-
- if (!file.delete()) {
- System.err.append("Failed to delete ").println(file.getAbsolutePath());
- }
-
- return file;
- }
-
- /**
- * Removes the specified file - if it is a directory, then its children
- * are deleted recursively and then the directory itself.
- *
- * @param path The file {@link Path} to be deleted - ignored if {@code null}
- * or does not exist anymore
- * @param options The {@link LinkOption}s to use
- * @return The <tt>path</tt> argument
- * @throws IOException If failed to access/remove some file(s)
- */
- public static Path deleteRecursive(Path path, LinkOption... options) throws IOException {
- if ((path == null) || (!Files.exists(path))) {
- return path;
- }
-
- if (Files.isDirectory(path)) {
- try (DirectoryStream<Path> ds = Files.newDirectoryStream(path)) {
- for (Path child : ds) {
- deleteRecursive(child, options);
- }
- }
- }
-
- try {
- // seems that if a file is not writable it cannot be deleted
- if (!Files.isWritable(path)) {
- path.toFile().setWritable(true, false);
- }
- Files.delete(path);
- } catch (IOException e) {
- // same logic as deleteRecursive(File) which does not check if deletion succeeded
- System.err.append("Failed (").append(e.getClass().getSimpleName()).append(")")
- .append(" to delete ").append(path.toString())
- .append(": ").println(e.getMessage());
- }
-
- return path;
- }
-
- /**
- * @param anchor An anchor {@link Class} whose container we want to use
- * as the starting point for the "target" folder lookup up the
- * hierarchy
- * @return The "target" <U>folder</U> - {@code null} if not found
- * @see #detectTargetFolder(File)
- */
- public static File detectTargetFolder(Class<?> anchor) {
- return detectTargetFolder(getClassContainerLocationFile(anchor));
- }
-
- /**
- * @param clazz A {@link Class} object
- * @return A {@link File} of the location of the class bytes container
- * - e.g., the root folder, the containing JAR, etc.. Returns
- * {@code null} if location could not be resolved
- * @throws IllegalArgumentException If location is not a valid
- * {@link File} location
- * @see #getClassContainerLocationURI(Class)
- * @see #toFileSource(URI)
- */
- public static File getClassContainerLocationFile(Class<?> clazz)
- throws IllegalArgumentException {
- try {
- URI uri = getClassContainerLocationURI(clazz);
- return toFileSource(uri);
- } catch (URISyntaxException | MalformedURLException e) {
- throw new IllegalArgumentException(e.getClass().getSimpleName() + ": " + e.getMessage(), e);
- }
- }
-
- /**
- * @param clazz A {@link Class} object
- * @return A {@link URI} to the location of the class bytes container
- * - e.g., the root folder, the containing JAR, etc.. Returns
- * {@code null} if location could not be resolved
- * @throws URISyntaxException if location is not a valid URI
- * @see #getClassContainerLocationURL(Class)
- */
- public static URI getClassContainerLocationURI(Class<?> clazz) throws URISyntaxException {
- URL url = getClassContainerLocationURL(clazz);
- return (url == null) ? null : url.toURI();
- }
-
- /**
- * @param clazz A {@link Class} object
- * @return A {@link URL} to the location of the class bytes container
- * - e.g., the root folder, the containing JAR, etc.. Returns
- * {@code null} if location could not be resolved
- */
- public static URL getClassContainerLocationURL(Class<?> clazz) {
- ProtectionDomain pd = clazz.getProtectionDomain();
- CodeSource cs = (pd == null) ? null : pd.getCodeSource();
- URL url = (cs == null) ? null : cs.getLocation();
- if (url == null) {
- url = getClassBytesURL(clazz);
- if (url == null) {
- return null;
- }
-
- String srcForm = getURLSource(url);
- if (GenericUtils.isEmpty(srcForm)) {
- return null;
- }
-
- try {
- url = new URL(srcForm);
- } catch (MalformedURLException e) {
- throw new IllegalArgumentException("getClassContainerLocationURL(" + clazz.getName() + ")"
- + " Failed to create URL=" + srcForm + " from " + url.toExternalForm()
- + ": " + e.getMessage());
- }
- }
-
- return url;
- }
-
- /**
- * Converts a {@link URL} that may refer to an internal resource to
- * a {@link File} representing is "source" container (e.g.,
- * if it is a resource in a JAR, then the result is the JAR's path)
- *
- * @param url The {@link URL} - ignored if {@code null}
- * @return The matching {@link File}
- * @throws MalformedURLException If source URL does not refer to a
- * file location
- * @see #toFileSource(URI)
- */
- public static File toFileSource(URL url) throws MalformedURLException {
- if (url == null) {
- return null;
- }
-
- try {
- return toFileSource(url.toURI());
- } catch (URISyntaxException e) {
- throw new MalformedURLException("toFileSource(" + url.toExternalForm() + ")"
- + " cannot (" + e.getClass().getSimpleName() + ")"
- + " convert to URI: " + e.getMessage());
- }
- }
-
- /**
- * Converts a {@link URI} that may refer to an internal resource to
- * a {@link File} representing is "source" container (e.g.,
- * if it is a resource in a JAR, then the result is the JAR's path)
- *
- * @param uri The {@link URI} - ignored if {@code null}
- * @return The matching {@link File}
- * @throws MalformedURLException If source URI does not refer to a
- * file location
- * @see #getURLSource(URI)
- */
- public static File toFileSource(URI uri) throws MalformedURLException {
- String src = getURLSource(uri);
- if (GenericUtils.isEmpty(src)) {
- return null;
- }
-
- if (!src.startsWith(FILE_URL_PREFIX)) {
- throw new MalformedURLException("toFileSource(" + src + ") not a '" + FILE_URL_SCHEME + "' scheme");
- }
-
- try {
- return new File(new URI(src));
- } catch (URISyntaxException e) {
- throw new MalformedURLException("toFileSource(" + src + ")"
- + " cannot (" + e.getClass().getSimpleName() + ")"
- + " convert to URI: " + e.getMessage());
- }
- }
-
- /**
- * @param uri The {@link URI} value - ignored if {@code null}
- * @return The URI(s) source path where {@link #JAR_URL_PREFIX} and
- * any sub-resource are stripped
- * @see #getURLSource(String)
- */
- public static String getURLSource(URI uri) {
- return getURLSource((uri == null) ? null : uri.toString());
- }
-
- /**
- * @param url The {@link URL} value - ignored if {@code null}
- * @return The URL(s) source path where {@link #JAR_URL_PREFIX} and
- * any sub-resource are stripped
- * @see #getURLSource(String)
- */
- public static String getURLSource(URL url) {
- return getURLSource((url == null) ? null : url.toExternalForm());
- }
-
- /**
- * @param externalForm The {@link URL#toExternalForm()} string - ignored if
- * {@code null}/empty
- * @return The URL(s) source path where {@link #JAR_URL_PREFIX} and
- * any sub-resource are stripped
- */
- public static String getURLSource(String externalForm) {
- String url = externalForm;
- if (GenericUtils.isEmpty(url)) {
- return url;
- }
-
- url = stripJarURLPrefix(externalForm);
- if (GenericUtils.isEmpty(url)) {
- return url;
- }
-
- int sepPos = url.indexOf(RESOURCE_SUBPATH_SEPARATOR);
- if (sepPos < 0) {
- return adjustURLPathValue(url);
- } else {
- return adjustURLPathValue(url.substring(0, sepPos));
- }
- }
-
- /**
- * @param url A {@link URL} - ignored if {@code null}
- * @return The path after stripping any trailing '/' provided the path
- * is not '/' itself
- * @see #adjustURLPathValue(String)
- */
- public static String adjustURLPathValue(URL url) {
- return adjustURLPathValue((url == null) ? null : url.getPath());
- }
-
- /**
- * @param path A URL path value - ignored if {@code null}/empty
- * @return The path after stripping any trailing '/' provided the path
- * is not '/' itself
- */
- public static String adjustURLPathValue(final String path) {
- final int pathLen = (path == null) ? 0 : path.length();
- if ((pathLen <= 1) || (path.charAt(pathLen - 1) != '/')) {
- return path;
- }
-
- return path.substring(0, pathLen - 1);
- }
-
- public static String stripJarURLPrefix(String externalForm) {
- String url = externalForm;
- if (GenericUtils.isEmpty(url)) {
- return url;
- }
-
- if (url.startsWith(JAR_URL_PREFIX)) {
- return url.substring(JAR_URL_PREFIX.length());
- }
-
- return url;
- }
-
- /**
- * @param clazz The request {@link Class}
- * @return A {@link URL} to the location of the <code>.class</code> file
- * - {@code null} if location could not be resolved
- */
- public static URL getClassBytesURL(Class<?> clazz) {
- String className = clazz.getName();
- int sepPos = className.indexOf('$');
- // if this is an internal class, then need to use its parent as well
- if (sepPos > 0) {
- sepPos = className.lastIndexOf('.');
- if (sepPos > 0) {
- className = className.substring(sepPos + 1);
- }
- } else {
- className = clazz.getSimpleName();
- }
-
- return clazz.getResource(className + CLASS_FILE_SUFFIX);
- }
-
- public static String getClassBytesResourceName(Class<?> clazz) {
- return getClassBytesResourceName((clazz == null) ? null : clazz.getName());
- }
-
- /**
- * @param name The fully qualified class name - ignored if {@code null}/empty
- * @return The relative path of the class file byte-code resource
- */
- public static String getClassBytesResourceName(String name) {
- if (GenericUtils.isEmpty(name)) {
- return name;
- } else {
- return name.replace('.', '/') + CLASS_FILE_SUFFIX;
- }
- }
-
- /**
- * @param anchorFile An anchor {@link File} we want to use
- * as the starting point for the "target" or "build" folder
- * lookup up the hierarchy
- * @return The "target" <U>folder</U> - {@code null} if not found
- */
- public static File detectTargetFolder(File anchorFile) {
- for (File file = anchorFile; file != null; file = file.getParentFile()) {
- if (!file.isDirectory()) {
- continue;
- }
-
- String name = file.getName();
- if (TARGET_FOLDER_NAMES.contains(name)) {
- return file;
- }
- }
-
- return null;
- }
-
- public static String resolveRelativeRemotePath(Path root, Path file) {
- Path relPath = root.relativize(file);
- return relPath.toString().replace(File.separatorChar, '/');
- }
-
- public static SshClient setupTestClient(Class<?> anchor) {
- SshClient client = SshClient.setUpDefaultClient();
- client.setServerKeyVerifier(AcceptAllServerKeyVerifier.INSTANCE);
- client.setHostConfigEntryResolver(HostConfigEntryResolver.EMPTY);
- client.setKeyPairProvider(KeyPairProvider.EMPTY_KEYPAIR_PROVIDER);
- return client;
- }
-
- public static SshServer setupTestServer(Class<?> anchor) {
- SshServer sshd = SshServer.setUpDefaultServer();
- sshd.setKeyPairProvider(createTestHostKeyProvider(anchor));
- sshd.setPasswordAuthenticator(BogusPasswordAuthenticator.INSTANCE);
- sshd.setPublickeyAuthenticator(AcceptAllPublickeyAuthenticator.INSTANCE);
- sshd.setShellFactory(EchoShellFactory.INSTANCE);
- sshd.setCommandFactory(UnknownCommandFactory.INSTANCE);
- return sshd;
- }
-
- /**
- * @param path The {@link Path} to write the data to
- * @param data The data to write (as UTF-8)
- * @return The UTF-8 data bytes
- * @throws IOException If failed to write
- */
- public static byte[] writeFile(Path path, String data) throws IOException {
- try (OutputStream fos = Files.newOutputStream(path, IoUtils.EMPTY_OPEN_OPTIONS)) {
- byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
- fos.write(bytes);
- return bytes;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadGlobalHostsConfigEntries.config.txt
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadGlobalHostsConfigEntries.config.txt b/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadGlobalHostsConfigEntries.config.txt
deleted file mode 100644
index 5f772cd..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadGlobalHostsConfigEntries.config.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-# Global section first
-
- User global
- Port 7365
- IdentityFile ~/.ssh/github.key
- HostName 37.77.34.7
-
-# User override
-Host *.apache.org
- User mina-sshd
-
-# Port override
-Host *.github.com
- Port 443
-
-# Host name override
-Host 10.*.*.*
- HostName 7.3.6.5
-
-# Identity override
-Host 192.168.*.*
- IdentityFile ~/.ssh/internal.key
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadMultipleHostPatterns.config.txt
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadMultipleHostPatterns.config.txt b/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadMultipleHostPatterns.config.txt
deleted file mode 100644
index 0f5edcb..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadMultipleHostPatterns.config.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-# Same configuration duplicated in multiple configuration entries
-Host *.github.com *.apache.org 10.*.*.*
- User mina-sshd
- Port 443
- HostName 7.3.6.5
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadSimpleHostsConfigEntries.config.txt
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadSimpleHostsConfigEntries.config.txt b/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadSimpleHostsConfigEntries.config.txt
deleted file mode 100644
index 1058777..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/client/config/hosts/testReadSimpleHostsConfigEntries.config.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-# Simple configuration file
-Host *.github.com
- User mina-sshd
- Port 443
- IdentityFile ~/.ssh/github.key
-
-Host *.apache.org
- User mina-sshd
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_dsa
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_dsa b/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_dsa
deleted file mode 100644
index 1d3ef24..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_dsa
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN DSA PRIVATE KEY-----
-MIIBvAIBAAKBgQDIPyMbBuQcZxeYDOyCqqkdK37cWQvp+RpWzvieB/oiG/ykfDQX
-oZMRtwqwWTBfejNitbBBmC6G/t5OK+9aFmj7pfJ+a7fZKXfiUquIg9soDsoOindf
-2AwR6MZ3os8uiP2xrC8IQAClnETa15mFShs4a4b2VjddgCQ6tphnY97MywIVAPtr
-YyW11RIXsVTf/9KlbhYaNlt5AoGAX9JzbHykC/0xDKOyKU6xDIOVdEZ0ooAl9Psl
-BEUuNhlv2XgmQScO6C9l2W7gbbut7zIw4FaZ2/dgXa3D4IyexBVug5XMnrssErZo
-NcoF5g0dgEAsb9Hl9gkIK3VHM5kWteeUg1VE700JTtSMisdL8CgIdR+xN8iVH5Ew
-CbLWxmECgYEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+ab
-YpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLa
-XWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7ACFQCE6flG
-nmVCAbzo9YsbdJWBnxMnBA==
------END DSA PRIVATE KEY-----
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_ecdsa
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_ecdsa b/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_ecdsa
deleted file mode 100644
index 31b1268..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_ecdsa
+++ /dev/null
@@ -1,5 +0,0 @@
------BEGIN EC PRIVATE KEY-----
-MHcCAQEEIPKmiQzAASg656IP4PuuElLdLdO/MIXrGxQG6tGkKZ1HoAoGCCqGSM49
-AwEHoUQDQgAEobHtw9wkL332ep9fi8Gw5g8sEGwslNonPUCDR6YUZ9mjOehliLpF
-DLHLxlIFafrVM+LIpagjpRKZcnpGPWQDnA==
------END EC PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_rsa_key
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_rsa_key b/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_rsa_key
deleted file mode 100644
index afc6aa8..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/client/config/keys/id_rsa_key
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEoQIBAAKCAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2c
-tGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZB
-gIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlE
-aaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl
-5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwh
-V+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMwIBIwKCAQALW02YHN4OJz1Siypj
-xoNi87slUaBKBF/NlkWauGUIcpZFMTwnkIn6vCz5MhRbQC4oadRDzFNUrC/g7HdH
-prlqYe2P7uEGIfMb3YNFdk3tgOHmRsHqFgFMpVWsOjlTxNTUsQ74N3Isuxnha4wY
-9f90sBULc+WRdRvO9jbkSDaqoYVKAqCFWtocL+ZWwBXWrIrsQW4PElgZ/duc5DX7
-eeJ5DXCSj9dO+1KxsWEOKaoeABEegrRVys1/shcDNPhf/p0QShKIdPcpnDUc8cny
-1bq8GSt6jEQ+tuRoSnYrY+RD+mlkHrx373Xc1a9woV+QKTThmd9TQ8gzHMHNqq0a
-7kR7AoGBAOuPOTRiKaDtQyMTEX7eeHsPNE24EgvONjNpxyQ6gKGthG5SiB0IO7mP
-r7EggbR2EY8eMCY5HjvxzxgH86n2Pqbzsr6UlQq7YTPupCm/7fPgRknu917GA20f
-1cuY8B04Jp4FIGryBmCcScX6usXXhjfAvYCWWfkSytA8gX9+b1TNAoGBANf8shbp
-wRnQfgAzw2S+xs29pdwa6Jb/xuLvHSyklmgidrK4nsVI8G+zeCqwkqkNM02sM+vR
-c8EX7+myrGf+S2V3JS3AMNXEhavrWVH0CuqFHlBjSwHZ0uKuPpWHlCnud+23AdQz
-Bf1H7tYKt5es3J/B37o4YxhAL6U9qq+ewZH/AoGBAOTURjLjA94oT9jt870SoOyS
-bVLQEYfPol3UeE8UQnEsN4Ec+UDGK2PNaNfzsTL2WjNB5aF5UJIA184znD60seQC
-raMxQFORdF5ViYekgMEFwJ+XrnlSpD4e7PGqgtqOUWZOH33VKsRADSa5DTU3xDYo
-8porp9wDoHKD64MqXYWTAoGADFeVJeF4v6WDivukw+2k9dBSntz3WRXuG5iilNY0
-evqnsnDzITdeMkTFCcDycA9iBHA9ezCKRYxW2id3kOn1rgbO7KvliI7jESNkMJGa
-OUlvck7RFgxyc1pp+ep9fr0rbKtfMLJ1Xu4q56jXSn7oCSEFeFr+WSg9PKRwJ0rm
-fV8CgYAkYOiNY8jH5lwwoPWOIPJg62zdzmRQktrU3z7Nzk5udN6JnG3g57QjkozX
-AgHARKQ2MuXW9OfOnYNhbGeavcBQmg5XUx3eL4PRFw8mFZdjpBD/kM/dfCEwEta3
-FpRlVGn0RNqVV5xxClObD/CikkDqKZG4MSj3CrO3JK33gl1Lgg==
------END RSA PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair
deleted file mode 100644
index 217d508..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair
+++ /dev/null
@@ -1,21 +0,0 @@
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsQAAAAdzc2gtZH
-NzAAAAgQD3axy1MBdh4TJC+T22DKKDe7sXnDrgJRs1jBcDl/qPdQDoLlvvTd8oHggXkIzI
-6Tx2ldiJ3KADMkDg1sBSU5zenIr9wisfEQOLNqfIOOirR9Z8jULGVlDO+pPwps0P9bcOka
-JCAfABNth6Oaz3NJjfFD8ZmmWDVaN2rSi1J3mjnwAAABUA5be00MaSti06Hor265V8Ggtj
-i/0AAACAa9MU8kBJ4Z1+UsrWruVr72eckEwjOPcRFeA6MoSM70GGdYVkLyQ78MMjKAFZcb
-seYT3lsja+c4LQ7yErROHSGFo+ImQnYGY3Cos2mS8tRBHtZ4YmKsBmS3Zrb4lEfauA1pgD
-pcGrUAavniKIAB/C9cpvj+IpZ36+rHpap4JKJ7kAAACAQP4NRzjtauzrDJvs73c0DPczAk
-LAan+90ouvIuI0CmMxFrSqLIIEaX3cB/P4dkKtBsMh0CxwJpoXfn+TO+1j7i08GuMONeqB
-3lOHn1/MB2qUAZh/kdU8RUsxWkrG80JMeP1kew1sHaoRfOUZ8+Xw/RL7nNmhhmHJR3rkgR
-8EoWEAAAHoMgIkejICJHoAAAAHc3NoLWRzcwAAAIEA92sctTAXYeEyQvk9tgyig3u7F5w6
-4CUbNYwXA5f6j3UA6C5b703fKB4IF5CMyOk8dpXYidygAzJA4NbAUlOc3pyK/cIrHxEDiz
-anyDjoq0fWfI1CxlZQzvqT8KbND/W3DpGiQgHwATbYejms9zSY3xQ/GZplg1Wjdq0otSd5
-o58AAAAVAOW3tNDGkrYtOh6K9uuVfBoLY4v9AAAAgGvTFPJASeGdflLK1q7la+9nnJBMIz
-j3ERXgOjKEjO9BhnWFZC8kO/DDIygBWXG7HmE95bI2vnOC0O8hK0Th0hhaPiJkJ2BmNwqL
-NpkvLUQR7WeGJirAZkt2a2+JRH2rgNaYA6XBq1AGr54iiAAfwvXKb4/iKWd+vqx6WqeCSi
-e5AAAAgED+DUc47Wrs6wyb7O93NAz3MwJCwGp/vdKLryLiNApjMRa0qiyCBGl93Afz+HZC
-rQbDIdAscCaaF35/kzvtY+4tPBrjDjXqgd5Th59fzAdqlAGYf5HVPEVLMVpKxvNCTHj9ZH
-sNbB2qEXzlGfPl8P0S+5zZoYZhyUd65IEfBKFhAAAAFGL9BNpShiQnNZ1noW8Woxyq1GuH
-AAAADnJvb3RAdWJ1bnR1LTE1AQIDBAU=
------END OPENSSH PRIVATE KEY-----
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair.pub
deleted file mode 100644
index c3feece..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-dss AAAAB3NzaC1kc3MAAACBAPdrHLUwF2HhMkL5PbYMooN7uxecOuAlGzWMFwOX+o91AOguW+9N3ygeCBeQjMjpPHaV2IncoAMyQODWwFJTnN6civ3CKx8RA4s2p8g46KtH1nyNQsZWUM76k/CmzQ/1tw6RokIB8AE22Ho5rPc0mN8UPxmaZYNVo3atKLUneaOfAAAAFQDlt7TQxpK2LToeivbrlXwaC2OL/QAAAIBr0xTyQEnhnX5Sytau5WvvZ5yQTCM49xEV4DoyhIzvQYZ1hWQvJDvwwyMoAVlxux5hPeWyNr5zgtDvIStE4dIYWj4iZCdgZjcKizaZLy1EEe1nhiYqwGZLdmtviUR9q4DWmAOlwatQBq+eIogAH8L1ym+P4ilnfr6selqngkonuQAAAIBA/g1HOO1q7OsMm+zvdzQM9zMCQsBqf73Si68i4jQKYzEWtKosggRpfdwH8/h2Qq0GwyHQLHAmmhd+f5M77WPuLTwa4w416oHeU4efX8wHapQBmH+R1TxFSzFaSsbzQkx4/WR7DWwdqhF85Rnz5fD9Evuc2aGGYclHeuSBHwShYQ== root@ubuntu-15
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair
deleted file mode 100644
index 403ff40..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS
-1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQAGRPv5eCmd3jFTCWrioWVHgQhHn/d
-ir8nriiEonZDPP+hEjX1AiYyahfvFWoqKI4lKRzoEmF5Wk6ct+9LM0JFGcEAck7Z3J/NXt
-CnHeEvnusHMoANjhKLExBURROOOTGziyHMuGBMBIgRFnf4rBhiTzduexJnaMglyqxIrDpG
-hOxwhQAAAAEQxPcsaMT3LGgAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ
-AAAIUEABkT7+Xgpnd4xUwlq4qFlR4EIR5/3Yq/J64ohKJ2Qzz/oRI19QImMmoX7xVqKiiO
-JSkc6BJheVpOnLfvSzNCRRnBAHJO2dyfzV7Qpx3hL57rBzKADY4SixMQVEUTjjkxs4shzL
-hgTASIERZ3+KwYYk83bnsSZ2jIJcqsSKw6RoTscIUAAAAAQQ+HCwVtvFlnRydGXZ+xpyKM
-KxDp5h7YMg5/dpRFrp3qNonm5/RHoT2Hw9i5GZtrXT2xPiR69wLOzTb4pnIWlENfAAAADn
-Jvb3RAdWJ1bnR1LTE1AQIDBAU=
------END OPENSSH PRIVATE KEY-----
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair.pub
deleted file mode 100644
index bd43737..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAAZE+/l4KZ3eMVMJauKhZUeBCEef92KvyeuKISidkM8/6ESNfUCJjJqF+8VaioojiUpHOgSYXlaTpy370szQkUZwQByTtncn81e0Kcd4S+e6wcygA2OEosTEFRFE445MbOLIcy4YEwEiBEWd/isGGJPN257EmdoyCXKrEisOkaE7HCFAA== root@ubuntu-15
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair
deleted file mode 100644
index 3176f3b..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair
+++ /dev/null
@@ -1,7 +0,0 @@
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
-QyNTUxOQAAACB0Tsaj0scs8fYKMlnz2Mncky545NoICP9eGMGIpo5G3QAAAJjCVtyJwlbc
-iQAAAAtzc2gtZWQyNTUxOQAAACB0Tsaj0scs8fYKMlnz2Mncky545NoICP9eGMGIpo5G3Q
-AAAEDjQpuV2OWHZVy7R09w6bw2DnBa1UdZrsAmQ7dPyxasx3ROxqPSxyzx9goyWfPYydyT
-Lnjk2ggI/14YwYimjkbdAAAADnJvb3RAdWJ1bnR1LTE1AQIDBAUGBw==
------END OPENSSH PRIVATE KEY-----
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair.pub
deleted file mode 100644
index 128e883..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHROxqPSxyzx9goyWfPYydyTLnjk2ggI/14YwYimjkbd root@ubuntu-15
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair
deleted file mode 100644
index 84ee354..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair
+++ /dev/null
@@ -1,49 +0,0 @@
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
-NhAAAAAwEAAQAAAgEArsY72FUUnK8yLqaY3WkotvMFm2aryoVVPbaHiXMqmKTTQPBFshwe
-Rk+wEJBE7th4fIq2MuTu+hMxEQ8+oYJ47Ka1+F96QE1F0uu1Tuec/ZpeuEiXsTBApWZ5Sa
-AMqQ3gMflfqgp96ipV3SOpLyEG9GIqVMJYNE/ggwIV7Uc4ybC21Zy3I7QOfHho8BeaMjV/
-703rlu2UWs8Twjo7VMWLvnGMUjNMzNGql9WouLEzHshmkUZBRdKvnTjsT9vx1q5OycaH/g
-Hos23EbNzn50rbiIm93PcZ/otGBjjfbHcsRmjxZhqCzLss5Nr93PLIePN8Cez91S7aLjCw
-Ri4ugKxK0EZwNHnHPi0yR0eI1eMfJLttdYnrOZiSxEVbCsZiDfTybuBYUDaH2EtgmzH9N2
-5IZeJOjrjE68NZhiwXnaIoHvfpkPTkWMSGLsLY3Kg0dYwUbAy9ErqkS/MEMmN70OxIxqTI
-hFSZ0bv3FBbZTZnl/GAEWmV+KSjURKuJlS9EBYE4ZvzYhGi6K7g3IQJZwrcqy6s8u24r5u
-qAQCihfuWFabq4nSYB1zczQ7Qq+V8wemak6lgoFGq4f58k3FUhkJbwQRFJoQYvaMI63dls
-9q67bkocjycsGPvWDU7IE9dVs36z7FevlQjsoHwAr4+0T1PFsqHn++KJ0p3AlopIV7ObS0
-cAAAdIyEoveMhKL3gAAAAHc3NoLXJzYQAAAgEArsY72FUUnK8yLqaY3WkotvMFm2aryoVV
-PbaHiXMqmKTTQPBFshweRk+wEJBE7th4fIq2MuTu+hMxEQ8+oYJ47Ka1+F96QE1F0uu1Tu
-ec/ZpeuEiXsTBApWZ5SaAMqQ3gMflfqgp96ipV3SOpLyEG9GIqVMJYNE/ggwIV7Uc4ybC2
-1Zy3I7QOfHho8BeaMjV/703rlu2UWs8Twjo7VMWLvnGMUjNMzNGql9WouLEzHshmkUZBRd
-KvnTjsT9vx1q5OycaH/gHos23EbNzn50rbiIm93PcZ/otGBjjfbHcsRmjxZhqCzLss5Nr9
-3PLIePN8Cez91S7aLjCwRi4ugKxK0EZwNHnHPi0yR0eI1eMfJLttdYnrOZiSxEVbCsZiDf
-TybuBYUDaH2EtgmzH9N25IZeJOjrjE68NZhiwXnaIoHvfpkPTkWMSGLsLY3Kg0dYwUbAy9
-ErqkS/MEMmN70OxIxqTIhFSZ0bv3FBbZTZnl/GAEWmV+KSjURKuJlS9EBYE4ZvzYhGi6K7
-g3IQJZwrcqy6s8u24r5uqAQCihfuWFabq4nSYB1zczQ7Qq+V8wemak6lgoFGq4f58k3FUh
-kJbwQRFJoQYvaMI63dls9q67bkocjycsGPvWDU7IE9dVs36z7FevlQjsoHwAr4+0T1PFsq
-Hn++KJ0p3AlopIV7ObS0cAAAADAQABAAACAEC2ca42Qn2JeSFA2lUQ/NxQu4DA0VcZa2iA
-T0sbc2g1j2r6DDRYqPULyHs5j+yIBoTGr5PEpJ+/v2k7pcsTjkbBq5sdbxyj//iLAgUHSV
-+1auD9L/2Rij4z5TDXrBhkZODcHwnM/LZZmpVZAOnMZtDSEC52D0a/VLfta8UBnAtB/VC2
-yDDWGI8J3oEXnCZ/HLG0vkiSC15cUkqWWSWYR5fqXzSIfiVQdMPnfqN0KSN6vE+KHd0Gl7
-O1QfqHy9HblJytf+kTN+BIAnT0joFbfFHf/ob0j5FqbDEdmcyFi8U+UjOmGUxI+eR7GYhX
-9NakAQeD0rC+ulZj/MDflKRcJw6+H/wHn2pdhahZY1A644SFin4SliFKl57njRkY/psjSg
-jpP0Nneg4+YFyFk9iUnmzxStJwuGE48I5eRESTFKgaMce5IVGOpz/YchQ9AL+ll2Qy6JfN
-nhpjuZD6Zh5KwsuLtCbaGfu8K8bbMEZg8F7jU6UdbpLmRLroKt6yFhffEp/tRrsX2hFoMt
-qCMpTEX2G+EzItPfFzq3/bFtnMxHmDiu4P8J03ALza/0OSIa4bdqMYG+97VBMbMeT6U76Q
-iYoDJH5/cdCBO/Cx+elM7oyFOFaV928h+6p01LGywcQdsgFPDyHSjXnF9t1fnLN4vhz5vU
-9QiVY7ldr+3PfxxpnRAAABAQDI87mazAnKsSJtkl70etg7vexltSxqkbhV/Hd5TYVMn19v
-U6rIjCY7q2xlHaUJdDNggwMpU679amkRP5BFPk9G4u8Xf5MyyDQpv+Hgpl7H0B1qEeJsBs
-huxURrX3EhLmb3300onEZXvlRwKOelIoVx5RZLUPhY0SbjBgH4sbO9NlYzs8xp4nLyK/jq
-DsqzmkkoQvK8zMybs3JwtGmTjDHvzmgcBgpm+s9gXyDJn3xe4CBW7v92N5/7eUehdXwomL
-4eDs/gR9QRb71bBSv9UaF5nK/TGJlysiM71JhBa2lmXlWg7KhALAu3SdTxxdvw0oyY+q6F
-E8zC0ljNMId8imVoAAABAQDi0m1WAW02b/IqwsJz0CAJXGpvhnhTSak3muZhTM0LPoavIe
-ow9KYL5Or4kQvbQXraYcaaRTp1Q9yJOrnXszAUWCGzqe38bLsI/SoRynLQaGEhdy1gr4tP
-WFKw0M34AshzyauEwHAVJIm6KZW/LgHsrxSXPFAABNQkQtB4olokNgSb/bysgo06OkkMBr
-N8mfY40m90RjTIvg3Cwei7xmna67MO1Bxzis8c7qQlf4hshFwnp+ixeLuhteYG1/DGxkfz
-yZ4UYXVKiuq3Xq+jFpYbKJeU17HL5TQFX34KCMN0BsMCcHLJ222IZkvFZj50715Z+jh/Vp
-xa6et++PX26BnVAAABAQDFQc26F12qnGA4heqa7Ig9WspGLEB4x5+cANOKaT/LtUFYWaYd
-O7Meeijkk+C1XcoOMac62QysRmjk8JEWdxgLiCFuKLp+6vxZLmEXBrBF0guHGv4/HlbpYn
-CxpMenxr0dhhwd2Flr1VtiadzEAfnc6MZE4byU8vVssYXORpVYIU46uI2OzIalnBhTXJYW
-KuamE/R+O1+dqnGgevQqUBkA5p4k8w+RMEY3hvSuNd6TFy5uoLXkCyOOpo60fGFVqMZyvJ
-IgU1et0umOA/JHrGzKHAl7WibRo4kf5MrPRLiDeDu92mqpwOl9NQjuTMR0OdZcXwOdrlG+
-daAgz5LetOKrAAAADnJvb3RAdWJ1bnR1LTE1AQIDBA==
------END OPENSSH PRIVATE KEY-----
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair.pub
deleted file mode 100644
index c59321c..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCuxjvYVRScrzIuppjdaSi28wWbZqvKhVU9toeJcyqYpNNA8EWyHB5GT7AQkETu2Hh8irYy5O76EzERDz6hgnjsprX4X3pATUXS67VO55z9ml64SJexMEClZnlJoAypDeAx+V+qCn3qKlXdI6kvIQb0YipUwlg0T+CDAhXtRzjJsLbVnLcjtA58eGjwF5oyNX/vTeuW7ZRazxPCOjtUxYu+cYxSM0zM0aqX1ai4sTMeyGaRRkFF0q+dOOxP2/HWrk7Jxof+AeizbcRs3OfnStuIib3c9xn+i0YGON9sdyxGaPFmGoLMuyzk2v3c8sh483wJ7P3VLtouMLBGLi6ArErQRnA0ecc+LTJHR4jV4x8ku211ies5mJLERVsKxmIN9PJu4FhQNofYS2CbMf03bkhl4k6OuMTrw1mGLBedoige9+mQ9ORYxIYuwtjcqDR1jBRsDL0SuqRL8wQyY3vQ7EjGpMiEVJnRu/cUFtlNmeX8YARaZX4pKNREq4mVL0QFgThm/NiEaLoruDchAlnCtyrLqzy7bivm6oBAKKF+5YVpuridJgHXNzNDtCr5XzB6ZqTqWCgUarh/nyTcVSGQlvBBEUmhBi9owjrd2Wz2rrtuShyPJywY+9YNTsgT11WzfrPsV6+VCOygfACvj7RPU8Wyoef74onSncCWikhXs5tLRw== root@ubuntu-15
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair b/sshd-core/src/test/resources/org/apache/sshd/common/util/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair
deleted file mode 100644
index 59af379..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair
+++ /dev/null
@@ -1,7 +0,0 @@
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
-QyNTUxOQAAACB0Tsaj0scs8fYKMlnz2Mncky545NoICP9eGMGIpo5G3QAAAJjCVtyJwlbc
-iQAAAAtzc2gtZWQyNTUxOQAAACB0Tsaj0scs8fYKMlnz2Mncky545NoICP9eGMGIpo5G3Q
-AAAEDjQpuV2OWHZVy7R09w6bw2DnBa1UdZrsAmQ7dPyxasx3ROxqPSxyzx9goyWfPYydyT
-Lnjk2ggI/14YwYimjkbdAAAADnJvb3RAdWJ1bnR1LTE1AQIDBAUGBw==
------END OPENSSH PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/resources/org/apache/sshd/common/util/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/resources/org/apache/sshd/common/util/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub b/sshd-core/src/test/resources/org/apache/sshd/common/util/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub
deleted file mode 100644
index 128e883..0000000
--- a/sshd-core/src/test/resources/org/apache/sshd/common/util/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub
+++ /dev/null
@@ -1 +0,0 @@
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHROxqPSxyzx9goyWfPYydyTLnjk2ggI/14YwYimjkbd root@ubuntu-15
\ No newline at end of file
[40/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
new file mode 100644
index 0000000..1620bad
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.lang.reflect.Proxy;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EventListener;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class EventListenerUtils {
+ /**
+ * A special "comparator" whose only purpose is to ensure
+ * there are no same references in a listener's set - to be used
+ * in conjunction with a {@code TreeSet} as its comparator
+ */
+ @SuppressWarnings("checkstyle:anoninnerlength")
+ public static final Comparator<EventListener> LISTENER_INSTANCE_COMPARATOR = (l1, l2) -> {
+ if (l1 == l2) {
+ return 0;
+ } else if (l1 == null) {
+ return 1;
+ } else if (l2 == null) {
+ return -1;
+ }
+
+ Class<?> c1 = l1.getClass();
+ Class<?> c2 = l2.getClass();
+ boolean checkHashCodes = true;
+ if (Proxy.isProxyClass(c1)) {
+ if (Proxy.isProxyClass(c2)) {
+ checkHashCodes = false; // cannot call hashCode on a proxy
+ } else {
+ return 1;
+ }
+ } else if (Proxy.isProxyClass(c2)) {
+ return -1;
+ }
+
+ if (checkHashCodes) {
+ int nRes = Integer.compare(l1.hashCode(), l2.hashCode());
+ if (nRes != 0) {
+ return nRes;
+ }
+ }
+
+ int nRes = Integer.compare(System.identityHashCode(l1), System.identityHashCode(l2));
+ if (nRes != 0) {
+ return nRes;
+ }
+
+ if (c1 != c2) {
+ return c1.getName().compareTo(c2.getName());
+ }
+
+ String s1 = Objects.toString(l1.toString(), "");
+ String s2 = Objects.toString(l2.toString(), "");
+ nRes = s1.compareTo(s2);
+ if (nRes != 0) {
+ return nRes;
+ }
+ throw new UnsupportedOperationException("Ran out of options to compare instance of " + s1 + " vs. " + s2);
+ };
+
+ private EventListenerUtils() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ /**
+ * @param <L> Type of {@link SshdEventListener} contained in the set
+ * @param listeners The listeners to pre-add to the create set - ignored
+ * if (@code null}/empty
+ * @return A (synchronized) {@link Set} for containing the listeners ensuring
+ * that if same listener instance is added repeatedly only <U>one</U>
+ * instance is actually contained
+ */
+ public static <L extends SshdEventListener> Set<L> synchronizedListenersSet(Collection<? extends L> listeners) {
+ Set<L> s = EventListenerUtils.synchronizedListenersSet();
+ if (GenericUtils.size(listeners) > 0) {
+ s.addAll(listeners);
+ }
+
+ return s;
+ }
+
+ /**
+ * @param <L> Type of {@link SshdEventListener} contained in the set
+ * @return A (synchronized) {@link Set} for containing the listeners ensuring
+ * that if same listener instance is added repeatedly only <U>one</U>
+ * instance is actually contained
+ * @see #LISTENER_INSTANCE_COMPARATOR
+ */
+ public static <L extends SshdEventListener> Set<L> synchronizedListenersSet() {
+ return Collections.synchronizedSet(new TreeSet<L>(LISTENER_INSTANCE_COMPARATOR));
+ }
+
+ /**
+ * Provides proxy wrapper around an {@link Iterable} container of listener
+ * interface implementation. <b>Note:</b> a listener interface is one whose
+ * invoked methods return <u>only</u> {@code void}.
+ *
+ * @param <T> Generic listener type
+ * @param listenerType The expected listener <u>interface</u>
+ * @param listeners An {@link Iterable} container of listeners to be invoked.
+ * <p>
+ * <b>Note(s):</b>
+ * </p>
+ * <ul>
+ * <li><p>
+ * The invocation order is same as the {@link Iterable} container
+ * </p></li>
+ *
+ * <li><p>
+ * If any of the invoked listener methods throws an exception, the
+ * rest of the listener are <u>not</u> invoked and the exception is
+ * propagated to the caller
+ * </p></li>
+ *
+ * <li><p>
+ * It is up to the <u>caller</u> to ensure that the container does
+ * not change while the proxy is invoked
+ * </p></li>
+ * </ul>
+ * @return A proxy wrapper implementing the same interface, but delegating
+ * the calls to the container
+ * @see #proxyWrapper(Class, ClassLoader, Iterable)
+ */
+ public static <T extends SshdEventListener> T proxyWrapper(Class<T> listenerType, Iterable<? extends T> listeners) {
+ return proxyWrapper(listenerType, listenerType.getClassLoader(), listeners);
+ }
+
+ /**
+ * Provides proxy wrapper around an {@link Iterable} container of listener
+ * interface implementation. <b>Note:</b> a listener interface is one whose
+ * invoked methods return <u>only</u> {@code void}.
+ *
+ * @param <T> Generic {@link SshdEventListener} type
+ * @param listenerType The expected listener <u>interface</u>
+ * @param loader The {@link ClassLoader} to use for the proxy
+ * @param listeners An {@link Iterable} container of listeners to be invoked.
+ * <p>
+ * <b>Note(s):</b>
+ * </p>
+ * <ul>
+ * <li><p>
+ * The invocation order is same as the {@link Iterable} container
+ * </p></li>
+ *
+ * <li><p>
+ * If any of the invoked listener methods throws an exception, the
+ * rest of the listener are <u>not</u> invoked and the exception is
+ * propagated to the caller
+ * </p></li>
+ *
+ * <li><p>
+ * It is up to the <u>caller</u> to ensure that the container does
+ * not change while the proxy is invoked
+ * </p></li>
+ * </ul>
+ * @return A proxy wrapper implementing the same interface, but delegating
+ * the calls to the container
+ * @throws IllegalArgumentException if <tt>listenerType</tt> is not an interface
+ * or a {@code null} container has been provided
+ * @see #proxyWrapper(Class, ClassLoader, Iterable)
+ */
+ public static <T extends SshdEventListener> T proxyWrapper(Class<T> listenerType, ClassLoader loader, final Iterable<? extends T> listeners) {
+ Objects.requireNonNull(listenerType, "No listener type specified");
+ ValidateUtils.checkTrue(listenerType.isInterface(), "Target proxy is not an interface: %s", listenerType.getSimpleName());
+ Objects.requireNonNull(listeners, "No listeners container provided");
+
+ Object wrapper = Proxy.newProxyInstance(loader, new Class<?>[]{listenerType}, (proxy, method, args) -> {
+ Throwable err = null;
+ for (T l : listeners) {
+ try {
+ method.invoke(l, args);
+ } catch (Throwable t) {
+ Throwable e = GenericUtils.peelException(t);
+ err = GenericUtils.accumulateException(err, e);
+ }
+ }
+
+ if (err != null) {
+ throw err;
+ }
+
+ return null; // we assume always void return value...
+ });
+ return listenerType.cast(wrapper);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/EventNotifier.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/EventNotifier.java b/sshd-common/src/main/java/org/apache/sshd/common/util/EventNotifier.java
new file mode 100644
index 0000000..c041f1b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/EventNotifier.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+/**
+ * Notify about the occurrence of an event
+ *
+ * @param <E> type of event being notified
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface EventNotifier<E> {
+ /**
+ * @param event The event
+ * @throws Exception If failed to process the event notification
+ */
+ void notifyEvent(E event) throws Exception;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
new file mode 100644
index 0000000..c924d1e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/GenericUtils.java
@@ -0,0 +1,915 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.NavigableSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.ExecutionException;
+import java.util.function.BinaryOperator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import javax.management.MBeanException;
+import javax.management.ReflectionException;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class GenericUtils {
+
+ public static final byte[] EMPTY_BYTE_ARRAY = {};
+ public static final char[] EMPTY_CHAR_ARRAY = {};
+ public static final String[] EMPTY_STRING_ARRAY = {};
+ public static final Object[] EMPTY_OBJECT_ARRAY = {};
+
+ /**
+ * A value indicating a {@code null} value - to be used as a placeholder
+ * where {@code null}s are not allowed
+ */
+ public static final Object NULL = new Object();
+
+ /**
+ * The complement of {@link String#CASE_INSENSITIVE_ORDER}
+ */
+ public static final Comparator<String> CASE_SENSITIVE_ORDER = (s1, s2) -> {
+ if (s1 == s2) {
+ return 0;
+ } else {
+ return s1.compareTo(s2);
+ }
+ };
+
+ public static final String QUOTES = "\"'";
+
+ @SuppressWarnings("rawtypes")
+ private static final Supplier CASE_INSENSITIVE_MAP_FACTORY = () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+ private GenericUtils() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ public static String trimToEmpty(String s) {
+ if (s == null) {
+ return "";
+ } else {
+ return s.trim();
+ }
+ }
+
+ public static String replaceWhitespaceAndTrim(String s) {
+ if (s != null) {
+ s = s.replace('\t', ' ');
+ }
+
+ return trimToEmpty(s);
+ }
+
+ /**
+ * @param s The {@link String} value to calculate the hash code on - may
+ * be {@code null}/empty in which case a value of zero is returned
+ * @return The calculated hash code
+ * @see #hashCode(String, Boolean)
+ */
+ public static int hashCode(String s) {
+ return hashCode(s, null);
+ }
+
+ /**
+ * @param s The {@link String} value to calculate the hash code on - may
+ * be {@code null}/empty in which case a value of zero is returned
+ * @param useUppercase Whether to convert the string to uppercase, lowercase
+ * or not at all:
+ * <UL>
+ * <LI>{@code null} - no conversion</LI>
+ * <LI>{@link Boolean#TRUE} - get hash code of uppercase</LI>
+ * <LI>{@link Boolean#FALSE} - get hash code of lowercase</LI>
+ * </UL>
+ * @return The calculated hash code
+ */
+ public static int hashCode(String s, Boolean useUppercase) {
+ if (isEmpty(s)) {
+ return 0;
+ } else if (useUppercase == null) {
+ return s.hashCode();
+ } else if (useUppercase.booleanValue()) {
+ return s.toUpperCase().hashCode();
+ } else {
+ return s.toLowerCase().hashCode();
+ }
+ }
+
+ public static int safeCompare(String s1, String s2, boolean caseSensitive) {
+ if (isSameReference(s1, s2)) {
+ return 0;
+ } else if (s1 == null) {
+ return +1; // push null(s) to end
+ } else if (s2 == null) {
+ return -1; // push null(s) to end
+ } else if (caseSensitive) {
+ return s1.compareTo(s2);
+ } else {
+ return s1.compareToIgnoreCase(s2);
+ }
+ }
+
+ public static <T> boolean isSameReference(T o1, T o2) {
+ return o1 == o2;
+ }
+
+ public static int length(CharSequence cs) {
+ return cs == null ? 0 : cs.length();
+ }
+
+ public static boolean isEmpty(CharSequence cs) {
+ return length(cs) <= 0;
+ }
+
+ public static boolean isNotEmpty(CharSequence cs) {
+ return !isEmpty(cs);
+ }
+
+ public static int indexOf(CharSequence cs, char c) {
+ int len = length(cs);
+ for (int pos = 0; pos < len; pos++) {
+ char ch = cs.charAt(pos);
+ if (ch == c) {
+ return pos;
+ }
+ }
+
+ return -1;
+ }
+
+ public static int lastIndexOf(CharSequence cs, char c) {
+ int len = length(cs);
+ for (int pos = len - 1; pos >= 0; pos--) {
+ char ch = cs.charAt(pos);
+ if (ch == c) {
+ return pos;
+ }
+ }
+
+ return -1;
+ }
+
+ // a List would be better, but we want to be compatible with String.split(...)
+ public static String[] split(String s, char ch) {
+ if (isEmpty(s)) {
+ return EMPTY_STRING_ARRAY;
+ }
+
+ int lastPos = 0;
+ int curPos = s.indexOf(ch);
+ if (curPos < 0) {
+ return new String[]{s};
+ }
+
+ Collection<String> values = new LinkedList<>();
+ do {
+ String v = s.substring(lastPos, curPos);
+ values.add(v);
+
+ // skip separator
+ lastPos = curPos + 1;
+ if (lastPos >= s.length()) {
+ break;
+ }
+
+ curPos = s.indexOf(ch, lastPos);
+ if (curPos < lastPos) {
+ break; // no more separators
+ }
+ } while (curPos < s.length());
+
+ // check if any leftovers
+ if (lastPos < s.length()) {
+ String v = s.substring(lastPos);
+ values.add(v);
+ }
+
+ return values.toArray(new String[values.size()]);
+ }
+
+ public static <T> String join(T[] values, char ch) {
+ return join(isEmpty(values) ? Collections.<T>emptyList() : Arrays.asList(values), ch);
+ }
+
+ public static String join(Iterable<?> iter, char ch) {
+ return join((iter == null) ? null : iter.iterator(), ch);
+ }
+
+ public static String join(Iterator<?> iter, char ch) {
+ if ((iter == null) || (!iter.hasNext())) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder();
+ do { // we already asked hasNext...
+ Object o = iter.next();
+ if (sb.length() > 0) {
+ sb.append(ch);
+ }
+ sb.append(Objects.toString(o));
+ } while (iter.hasNext());
+
+ return sb.toString();
+ }
+
+ public static <T> String join(T[] values, CharSequence sep) {
+ return join(isEmpty(values) ? Collections.<T>emptyList() : Arrays.asList(values), sep);
+ }
+
+ public static String join(Iterable<?> iter, CharSequence sep) {
+ return join((iter == null) ? null : iter.iterator(), sep);
+ }
+
+ public static String join(Iterator<?> iter, CharSequence sep) {
+ if ((iter == null) || (!iter.hasNext())) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder();
+ do { // we already asked hasNext...
+ Object o = iter.next();
+ if (sb.length() > 0) {
+ sb.append(sep);
+ }
+ sb.append(Objects.toString(o));
+ } while (iter.hasNext());
+
+ return sb.toString();
+ }
+
+ public static int size(Collection<?> c) {
+ return c == null ? 0 : c.size();
+ }
+
+ public static boolean isEmpty(Collection<?> c) {
+ return (c == null) || c.isEmpty();
+ }
+
+ public static boolean isNotEmpty(Collection<?> c) {
+ return !isEmpty(c);
+ }
+
+ public static int size(Map<?, ?> m) {
+ return m == null ? 0 : m.size();
+ }
+
+ public static boolean isEmpty(Map<?, ?> m) {
+ return (m == null) || m.isEmpty();
+ }
+
+ public static boolean isNotEmpty(Map<?, ?> m) {
+ return !isEmpty(m);
+ }
+
+ @SafeVarargs
+ public static <T> int length(T... a) {
+ return a == null ? 0 : a.length;
+ }
+
+ public static <T> boolean isEmpty(Iterable<? extends T> iter) {
+ if (iter == null) {
+ return true;
+ } else if (iter instanceof Collection<?>) {
+ return isEmpty((Collection<?>) iter);
+ } else {
+ return isEmpty(iter.iterator());
+ }
+ }
+
+ public static <T> boolean isNotEmpty(Iterable<? extends T> iter) {
+ return !isEmpty(iter);
+ }
+
+ public static <T> boolean isEmpty(Iterator<? extends T> iter) {
+ return iter == null || !iter.hasNext();
+ }
+
+ public static <T> boolean isNotEmpty(Iterator<? extends T> iter) {
+ return !isEmpty(iter);
+ }
+
+ @SafeVarargs
+ public static <T> boolean isEmpty(T... a) {
+ return length(a) <= 0;
+ }
+
+ public static int length(char[] chars) {
+ return (chars == null) ? 0 : chars.length;
+ }
+
+ public static boolean isEmpty(char[] chars) {
+ return length(chars) <= 0;
+ }
+
+ /**
+ * Compares 2 character arrays - <B>Note:</B> {@code null} and empty
+ * are considered <U>equal</U>
+ *
+ * @param c1 1st array
+ * @param c2 2nd array
+ * @return Negative is 1st array comes first in lexicographical order,
+ * positive if 2nd array comes first and zero if equal
+ */
+ public static int compare(char[] c1, char[] c2) {
+ int l1 = length(c1);
+ int l2 = length(c2);
+ int cmpLen = Math.min(l1, l2);
+ for (int index = 0; index < cmpLen; index++) {
+ char c11 = c1[index];
+ char c22 = c2[index];
+ int nRes = Character.compare(c11, c22);
+ if (nRes != 0) {
+ return nRes;
+ }
+ }
+
+ int nRes = Integer.compare(l1, l2);
+ if (nRes != 0) {
+ return nRes;
+ }
+
+ return 0;
+ }
+
+ @SafeVarargs // there is no EnumSet.of(...) so we have to provide our own
+ public static <E extends Enum<E>> Set<E> of(E... values) {
+ return of(isEmpty(values) ? Collections.emptySet() : Arrays.asList(values));
+ }
+
+ public static <E extends Enum<E>> Set<E> of(Collection<? extends E> values) {
+ if (isEmpty(values)) {
+ return Collections.emptySet();
+ }
+
+ Set<E> result = null;
+ for (E v : values) {
+ /*
+ * A trick to compensate for the fact that we do not have
+ * the enum Class to invoke EnumSet.noneOf
+ */
+ if (result == null) {
+ result = EnumSet.of(v);
+ } else {
+ result.add(v);
+ }
+ }
+
+ return result;
+ }
+
+ public static <T> boolean containsAny(Collection<? extends T> coll, Iterable<? extends T> values) {
+ if (isEmpty(coll)) {
+ return false;
+ }
+
+ for (T v : values) {
+ if (coll.contains(v)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static <T> void forEach(Iterable<T> values, Consumer<T> consumer) {
+ if (isNotEmpty(values)) {
+ values.forEach(consumer);
+ }
+ }
+
+ public static <T, U> List<U> map(Collection<T> values, Function<? super T, ? extends U> mapper) {
+ return stream(values).map(mapper).collect(Collectors.toList());
+ }
+
+ public static <T, U> NavigableSet<U> mapSort(
+ Collection<T> values, Function<? super T, ? extends U> mapper, Comparator<U> comparator) {
+ return stream(values).map(mapper).collect(toSortedSet(comparator));
+ }
+
+ public static <T, K, U> NavigableMap<K, U> toSortedMap(
+ Iterable<T> values, Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, Comparator<K> comparator) {
+ return stream(values).collect(toSortedMap(keyMapper, valueMapper, comparator));
+ }
+
+ public static <T, K, U> Collector<T, ?, NavigableMap<K, U>> toSortedMap(
+ Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, Comparator<K> comparator) {
+ return Collectors.toMap(keyMapper, valueMapper, throwingMerger(), () -> new TreeMap<>(comparator));
+ }
+
+ private static <T> BinaryOperator<T> throwingMerger() {
+ return (u, v) -> {
+ throw new IllegalStateException(String.format("Duplicate key %s", u));
+ };
+ }
+
+ public static <T> Collector<T, ?, NavigableSet<T>> toSortedSet(Comparator<T> comparator) {
+ return Collectors.toCollection(() -> new TreeSet<>(comparator));
+ }
+
+ public static <T> Stream<T> stream(Iterable<T> values) {
+ if (isEmpty(values)) {
+ return Stream.empty();
+ } else if (values instanceof Collection<?>) {
+ return ((Collection<T>) values).stream();
+ } else {
+ return StreamSupport.stream(values.spliterator(), false);
+ }
+ }
+
+ @SafeVarargs
+ public static <T> List<T> unmodifiableList(T... values) {
+ return unmodifiableList(asList(values));
+ }
+
+ public static <T> List<T> unmodifiableList(Collection<? extends T> values) {
+ if (isEmpty(values)) {
+ return Collections.emptyList();
+ } else {
+ return Collections.unmodifiableList(new ArrayList<>(values));
+ }
+ }
+
+ public static <T> List<T> unmodifiableList(Stream<T> values) {
+ return unmodifiableList(values.collect(Collectors.toList()));
+ }
+
+ @SafeVarargs
+ public static <T> List<T> asList(T... values) {
+ return isEmpty(values) ? Collections.emptyList() : Arrays.asList(values);
+ }
+
+ @SafeVarargs
+ public static <T> Set<T> asSet(T... values) {
+ return new HashSet<>(asList(values));
+ }
+
+ @SafeVarargs
+ public static <V extends Comparable<V>> NavigableSet<V> asSortedSet(V... values) {
+ return asSortedSet(Comparator.naturalOrder(), values);
+ }
+
+ public static <V extends Comparable<V>> NavigableSet<V> asSortedSet(Collection<? extends V> values) {
+ return asSortedSet(Comparator.naturalOrder(), values);
+ }
+
+ /**
+ * @param <V> The element type
+ * @param comp The (non-{@code null}) {@link Comparator} to use
+ * @param values The values to be added (ignored if {@code null})
+ * @return A {@link NavigableSet} containing the values (if any) sorted
+ * using the provided comparator
+ */
+ @SafeVarargs
+ public static <V> NavigableSet<V> asSortedSet(Comparator<? super V> comp, V... values) {
+ return asSortedSet(comp, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
+ }
+
+ /**
+ * @param <V> The element type
+ * @param comp The (non-{@code null}) {@link Comparator} to use
+ * @param values The values to be added (ignored if {@code null}/empty)
+ * @return A {@link NavigableSet} containing the values (if any) sorted
+ * using the provided comparator
+ */
+ public static <V> NavigableSet<V> asSortedSet(Comparator<? super V> comp, Collection<? extends V> values) {
+ NavigableSet<V> set = new TreeSet<>(Objects.requireNonNull(comp, "No comparator"));
+ if (size(values) > 0) {
+ set.addAll(values);
+ }
+ return set;
+ }
+
+ /**
+ * @param <V> Type of mapped value
+ * @return A {@link Supplier} that returns a <U>new</U> {@link NavigableMap}
+ * whenever its {@code get()} method is invoked
+ */
+ @SuppressWarnings("unchecked")
+ public static <V> Supplier<NavigableMap<String, V>> caseInsensitiveMap() {
+ return CASE_INSENSITIVE_MAP_FACTORY;
+ }
+
+ /**
+ * Flips between keys and values of an input map
+ *
+ * @param <K> Original map key type
+ * @param <V> Original map value type
+ * @param <M> Flipped map type
+ * @param map The original map to flip
+ * @param mapCreator The creator of the target map
+ * @param allowDuplicates Whether to ignore duplicates on flip
+ * @return The flipped map result
+ * @throws IllegalArgumentException if <tt>allowDuplicates</tt> is {@code false}
+ * and a duplicate value found in the original map.
+ */
+ public static <K, V, M extends Map<V, K>> M flipMap(
+ Map<? extends K, ? extends V> map, Supplier<? extends M> mapCreator, boolean allowDuplicates) {
+ M result = Objects.requireNonNull(mapCreator.get(), "No map created");
+ map.forEach((key, value) -> {
+ K prev = result.put(value, key);
+ if ((prev != null) && (!allowDuplicates)) {
+ ValidateUtils.throwIllegalArgumentException("Multiple values for key=%s: current=%s, previous=%s", value, key, prev);
+ }
+ });
+
+ return result;
+ }
+
+ @SafeVarargs
+ public static <K, V, M extends Map<K, V>> M mapValues(
+ Function<? super V, ? extends K> keyMapper, Supplier<? extends M> mapCreator, V... values) {
+ return mapValues(keyMapper, mapCreator, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
+ }
+
+ /**
+ * Creates a map out of a group of values
+ *
+ * @param <K> The key type
+ * @param <V> The value type
+ * @param <M> The result {@link Map} type
+ * @param keyMapper The {@link Function} that generates a key for a given value.
+ * If the returned key is {@code null} then the value is not mapped
+ * @param mapCreator The {@link Supplier} used to create/retrieve the result map - provided
+ * non-empty group of values
+ * @param values The values to be mapped
+ * @return The resulting {@link Map} - <B>Note:</B> no validation is made to ensure
+ * that 2 (or more) values are not mapped to the same key
+ */
+ public static <K, V, M extends Map<K, V>> M mapValues(
+ Function<? super V, ? extends K> keyMapper, Supplier<? extends M> mapCreator, Collection<? extends V> values) {
+ M map = mapCreator.get();
+ for (V v : values) {
+ K k = keyMapper.apply(v);
+ if (k == null) {
+ continue; // debug breakpoint
+ }
+ map.put(k, v);
+ }
+
+ return map;
+ }
+
+ @SafeVarargs
+ public static <T> T findFirstMatchingMember(Predicate<? super T> acceptor, T... values) {
+ return findFirstMatchingMember(acceptor, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
+ }
+
+ public static <T> T findFirstMatchingMember(Predicate<? super T> acceptor, Collection<? extends T> values) {
+ List<T> matches = selectMatchingMembers(acceptor, values);
+ return GenericUtils.isEmpty(matches) ? null : matches.get(0);
+ }
+
+ /**
+ * Returns a list of all the values that were accepted by a predicate
+ *
+ * @param <T> The type of value being evaluated
+ * @param acceptor The {@link Predicate} to consult whether a member is selected
+ * @param values The values to be scanned
+ * @return A {@link List} of all the values that were accepted by the predicate
+ */
+ @SafeVarargs
+ public static <T> List<T> selectMatchingMembers(Predicate<? super T> acceptor, T... values) {
+ return selectMatchingMembers(acceptor, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
+ }
+
+ /**
+ * Returns a list of all the values that were accepted by a predicate
+ *
+ * @param <T> The type of value being evaluated
+ * @param acceptor The {@link Predicate} to consult whether a member is selected
+ * @param values The values to be scanned
+ * @return A {@link List} of all the values that were accepted by the predicate
+ */
+ public static <T> List<T> selectMatchingMembers(Predicate<? super T> acceptor, Collection<? extends T> values) {
+ return GenericUtils.stream(values)
+ .filter(acceptor)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * @param s The {@link CharSequence} to be checked
+ * @return If the sequence contains any of the {@link #QUOTES}
+ * on <U>both</U> ends, then they are stripped, otherwise
+ * nothing is done
+ * @see #stripDelimiters(CharSequence, char)
+ */
+ public static CharSequence stripQuotes(CharSequence s) {
+ if (isEmpty(s)) {
+ return s;
+ }
+
+ for (int index = 0; index < QUOTES.length(); index++) {
+ char delim = QUOTES.charAt(index);
+ CharSequence v = stripDelimiters(s, delim);
+ if (v != s) { // if stripped one don't continue
+ return v;
+ }
+ }
+
+ return s;
+ }
+
+ /**
+ * @param s The {@link CharSequence} to be checked
+ * @param delim The expected delimiter
+ * @return If the sequence contains the delimiter on <U>both</U> ends,
+ * then it is are stripped, otherwise nothing is done
+ */
+ public static CharSequence stripDelimiters(CharSequence s, char delim) {
+ if (isEmpty(s) || (s.length() < 2)) {
+ return s;
+ }
+
+ int lastPos = s.length() - 1;
+ if ((s.charAt(0) != delim) || (s.charAt(lastPos) != delim)) {
+ return s;
+ } else {
+ return s.subSequence(1, lastPos);
+ }
+ }
+
+ public static RuntimeException toRuntimeException(Throwable t) {
+ return toRuntimeException(t, true);
+ }
+
+ /**
+ * Converts a thrown generic exception to a {@link RuntimeException}
+ *
+ * @param t The original thrown exception
+ * @param peelThrowable Whether to determine the root cause by "peeling"
+ * any enclosing exceptions
+ * @return The thrown cause if already a runtime exception, otherwise a
+ * runtime exception of the resolved exception as its cause
+ * @see #peelException(Throwable)
+ */
+ public static RuntimeException toRuntimeException(Throwable t, boolean peelThrowable) {
+ Throwable e = peelThrowable ? peelException(t) : t;
+ if (e instanceof RuntimeException) {
+ return (RuntimeException) e;
+ }
+
+ return new RuntimeException(e);
+ }
+
+ /**
+ * Attempts to get to the "effective" exception being thrown,
+ * by taking care of some known exceptions that wrap the original thrown
+ * one.
+ *
+ * @param t The original {@link Throwable} - ignored if {@code null}
+ * @return The effective exception - same as input if not a wrapper
+ */
+ public static Throwable peelException(Throwable t) {
+ // NOTE: check order is important - e.g., InvocationTargetException extends ReflectiveOperationException
+ if (t == null) {
+ return t;
+ } else if (t instanceof UndeclaredThrowableException) {
+ Throwable wrapped = ((UndeclaredThrowableException) t).getUndeclaredThrowable();
+ // according to the Javadoc it may be null, in which case 'getCause'
+ // might contain the information we need
+ if (wrapped != null) {
+ return peelException(wrapped);
+ }
+
+ wrapped = t.getCause();
+ if (wrapped != t) { // make sure it is a real cause
+ return peelException(wrapped);
+ }
+ } else if (t instanceof InvocationTargetException) {
+ Throwable target = ((InvocationTargetException) t).getTargetException();
+ if (target != null) {
+ return peelException(target);
+ }
+ } else if (t instanceof ReflectionException) {
+ Throwable target = ((ReflectionException) t).getTargetException();
+ if (target != null) {
+ return peelException(target);
+ }
+ } else if (t instanceof ExecutionException) {
+ Throwable wrapped = resolveExceptionCause(t);
+ if (wrapped != null) {
+ return peelException(wrapped);
+ }
+ } else if (t instanceof MBeanException) {
+ Throwable target = ((MBeanException) t).getTargetException();
+ if (target != null) {
+ return peelException(target);
+ }
+ }
+
+ return t; // no special handling required or available
+ }
+
+ /**
+ * @param t The original {@link Throwable} - ignored if {@code null}
+ * @return If {@link Throwable#getCause()} is non-{@code null} then
+ * the cause, otherwise the original exception - {@code null} if
+ * the original exception was {@code null}
+ */
+ public static Throwable resolveExceptionCause(Throwable t) {
+ if (t == null) {
+ return t;
+ }
+
+ Throwable c = t.getCause();
+ if (c == null) {
+ return t;
+ } else {
+ return c;
+ }
+ }
+
+ /**
+ * Used to "accumulate" exceptions of the <U>same type</U>. If the
+ * current exception is {@code null} then the new one becomes the current,
+ * otherwise the new one is added as a <U>suppressed</U> exception to the
+ * current one
+ *
+ * @param <T> The exception type
+ * @param current The current exception
+ * @param extra The extra/new exception
+ * @return The resolved exception
+ * @see Throwable#addSuppressed(Throwable)
+ */
+ public static <T extends Throwable> T accumulateException(T current, T extra) {
+ if (current == null) {
+ return extra;
+ }
+
+ if ((extra == null) || (extra == current)) {
+ return current;
+ }
+
+ current.addSuppressed(extra);
+ return current;
+ }
+
+ public static IOException toIOException(Throwable e) {
+ if (e instanceof IOException) {
+ return (IOException) e;
+ } else {
+ return new IOException(e);
+ }
+ }
+
+ /**
+ * Wraps a value into a {@link Supplier}
+ * @param <T> Type of value being supplied
+ * @param value The value to be supplied
+ * @return The supplier wrapper
+ */
+ public static <T> Supplier<T> supplierOf(T value) {
+ return () -> value;
+ }
+
+ /**
+ * Resolves to an always non-{@code null} iterator
+ *
+ * @param <T> Type of value being iterated
+ * @param iterable The {@link Iterable} instance
+ * @return A non-{@code null} iterator which may be empty if no iterable
+ * instance or no iterator returned from it
+ * @see #iteratorOf(Iterator)
+ */
+ public static <T> Iterator<T> iteratorOf(Iterable<T> iterable) {
+ return iteratorOf((iterable == null) ? null : iterable.iterator());
+ }
+
+ /**
+ * @param <B> Generic base class
+ * @param <D> Generic child class
+ * @return An identity {@link Function} that returns its input child class as a base class
+ */
+ public static <B, D extends B> Function<D, B> downcast() {
+ return t -> t;
+ }
+
+ /**
+ * Resolves to an always non-{@code null} iterator
+ *
+ * @param <T> Type of value being iterated
+ * @param iter The {@link Iterator} instance
+ * @return A non-{@code null} iterator which may be empty if no iterator instance
+ * @see Collections#emptyIterator()
+ */
+ public static <T> Iterator<T> iteratorOf(Iterator<T> iter) {
+ return (iter == null) ? Collections.emptyIterator() : iter;
+ }
+
+ public static <U, V> Iterable<V> wrapIterable(Iterable<? extends U> iter, Function<? super U, ? extends V> mapper) {
+ return () -> wrapIterator(iter, mapper);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public static <U, V> Iterator<V> wrapIterator(Iterable<? extends U> iter, Function<? super U, ? extends V> mapper) {
+ return (Iterator) stream(iter).map(mapper).iterator();
+ }
+
+ public static <U, V> Iterator<V> wrapIterator(Iterator<? extends U> iter, Function<? super U, ? extends V> mapper) {
+ Iterator<? extends U> iterator = iteratorOf(iter);
+ return new Iterator<V>() {
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public V next() {
+ U value = iterator.next();
+ return mapper.apply(value);
+ }
+ };
+ }
+
+ /**
+ * Wraps a group of {@link Supplier}s of {@link Iterable} instances into a "unified"
+ * {@link Iterable} of their values, in the same order as the suppliers - i.e., once the values
+ * from a specific supplier are exhausted, the next one is consulted, and so on, until all
+ * suppliers have been consulted
+ *
+ * @param <T> Type of value being iterated
+ * @param providers The providers - ignored if {@code null} (i.e., return an empty iterable instance)
+ * @return The wrapping instance
+ */
+ public static <T> Iterable<T> multiIterableSuppliers(Iterable<? extends Supplier<? extends Iterable<? extends T>>> providers) {
+ return () -> stream(providers).<T>flatMap(s -> stream(s.get())).map(Function.identity()).iterator();
+ }
+
+ public static <K, V> MapBuilder<K, V> mapBuilder() {
+ return new MapBuilder<>();
+ }
+
+ public static <K, V> MapBuilder<K, V> mapBuilder(Comparator<K> comparator) {
+ return new MapBuilder<>(comparator);
+ }
+
+ public static class MapBuilder<K, V> {
+ private Map<K, V> map;
+
+ public MapBuilder() {
+ this.map = new LinkedHashMap<>();
+ }
+
+ public MapBuilder(Comparator<? super K> comparator) {
+ this.map = new TreeMap<>(comparator);
+ }
+
+ public MapBuilder<K, V> put(K k, V v) {
+ map.put(k, v);
+ return this;
+ }
+
+ public Map<K, V> build() {
+ return map;
+ }
+
+ public Map<K, V> immutable() {
+ return Collections.unmodifiableMap(map);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java b/sshd-common/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
new file mode 100644
index 0000000..8f18bfe
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A dummy map that ignores all {@code put/remove} calls
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class IgnoringEmptyMap<K, V> implements Map<K, V> {
+ @SuppressWarnings("rawtypes")
+ private static final IgnoringEmptyMap INSTANCE = new IgnoringEmptyMap();
+
+ public IgnoringEmptyMap() {
+ super();
+ }
+
+ @Override
+ public int size() {
+ return 0;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return true;
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ Objects.requireNonNull(value, "No value provided");
+ return false;
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ Objects.requireNonNull(key, "No key provided");
+ return false;
+ }
+
+ @Override
+ public V get(Object key) {
+ Objects.requireNonNull(key, "No key provided");
+ return null;
+ }
+
+ @Override
+ public V put(K key, V value) {
+ Objects.requireNonNull(key, "No key provided");
+ Objects.requireNonNull(value, "No value provided");
+ return null;
+ }
+
+ @Override
+ public V remove(Object key) {
+ Objects.requireNonNull(key, "No key provided");
+ return null;
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ // ignored
+ }
+
+ @Override
+ public void clear() {
+ // ignored
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Collection<V> values() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof IgnoringEmptyMap<?, ?>;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "{}";
+ }
+
+ @Override
+ public Set<Entry<K, V>> entrySet() {
+ return Collections.emptySet();
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <K, V> IgnoringEmptyMap<K, V> getInstance() {
+ return INSTANCE;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java b/sshd-common/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java
new file mode 100644
index 0000000..490abb0
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.function.IntUnaryOperator;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class Int2IntFunction {
+ private Int2IntFunction() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ public static IntUnaryOperator sub(int delta) {
+ return add(0 - delta);
+ }
+
+ public static IntUnaryOperator add(int delta) {
+ if (delta == 0) {
+ return IntUnaryOperator.identity();
+ } else {
+ return value -> value + delta;
+ }
+ }
+
+ public static IntUnaryOperator mul(int factor) {
+ if (factor == 0) {
+ return constant(0);
+ } else if (factor == 1) {
+ return IntUnaryOperator.identity();
+ } else {
+ return value -> value * factor;
+ }
+ }
+
+ public static IntUnaryOperator constant(int v) {
+ return value -> v;
+ }
+
+ public static IntUnaryOperator div(int factor) {
+ if (factor == 1) {
+ return IntUnaryOperator.identity();
+ } else {
+ ValidateUtils.checkTrue(factor != 0, "Zero division factor");
+ return value -> value / factor;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/Invoker.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/Invoker.java b/sshd-common/src/main/java/org/apache/sshd/common/util/Invoker.java
new file mode 100644
index 0000000..71cbebd
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/Invoker.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * The complement to the {@code Callable} interface - accepts one argument
+ * and possibly throws somethind
+ *
+ * @param <ARG> Argument type
+ * @param <RET> Return type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface Invoker<ARG, RET> {
+ RET invoke(ARG arg) throws Throwable;
+
+ static <ARG> Invoker<ARG, Void> wrapAll(Collection<? extends Invoker<? super ARG, ?>> invokers) {
+ return arg -> {
+ invokeAll(arg, invokers);
+ return null;
+ };
+ }
+
+ /**
+ * Invokes <U>all</U> the instances ignoring the return value. Any
+ * intermediate exceptions are accumulated and thrown at the end.
+ *
+ * @param <ARG> Argument type
+ * @param arg The argument to pass to the {@link #invoke(Object)} method
+ * @param invokers The invokers to scan - ignored if {@code null}/empty
+ * (also ignores {@code null} members)
+ * @throws Throwable If invocation failed
+ */
+ static <ARG> void invokeAll(ARG arg, Collection<? extends Invoker<? super ARG, ?>> invokers) throws Throwable {
+ if (GenericUtils.isEmpty(invokers)) {
+ return;
+ }
+
+ Throwable err = null;
+ for (Invoker<? super ARG, ?> i : invokers) {
+ if (i == null) {
+ continue;
+ }
+
+ try {
+ i.invoke(arg);
+ } catch (Throwable t) {
+ err = GenericUtils.accumulateException(err, t);
+ }
+ }
+
+ if (err != null) {
+ throw err;
+ }
+ }
+
+ static <ARG> Invoker<ARG, Void> wrapFirst(Collection<? extends Invoker<? super ARG, ?>> invokers) {
+ return arg -> {
+ Map.Entry<Invoker<? super ARG, ?>, Throwable> result = invokeTillFirstFailure(arg, invokers);
+ if (result != null) {
+ throw result.getValue();
+ }
+ return null;
+ };
+ }
+
+ /**
+ * Invokes all instances until 1st failure (if any)
+ *
+ * @param <ARG> Argument type
+ * @param arg The argument to pass to the {@link #invoke(Object)} method
+ * @param invokers The invokers to scan - ignored if {@code null}/empty
+ * (also ignores {@code null} members)
+ * @return A {@link SimpleImmutableEntry} representing the <U>first</U> failed
+ * invocation - {@code null} if all were successful (or none invoked).
+ */
+ static <ARG> SimpleImmutableEntry<Invoker<? super ARG, ?>, Throwable> invokeTillFirstFailure(ARG arg, Collection<? extends Invoker<? super ARG, ?>> invokers) {
+ if (GenericUtils.isEmpty(invokers)) {
+ return null;
+ }
+
+ for (Invoker<? super ARG, ?> i : invokers) {
+ if (i == null) {
+ continue;
+ }
+
+ try {
+ i.invoke(arg);
+ } catch (Throwable t) {
+ return new SimpleImmutableEntry<>(i, t);
+ }
+ }
+
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java
new file mode 100644
index 0000000..6bcc93a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.util.Comparator;
+import java.util.Map;
+
+/**
+ * Represents an un-modifiable pair of values
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class MapEntryUtils {
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private static final Comparator<Map.Entry<Comparable, ?>> BY_KEY_COMPARATOR = (o1, o2) -> {
+ Comparable k1 = o1.getKey();
+ Comparable k2 = o2.getKey();
+ return k1.compareTo(k2);
+ };
+
+ private MapEntryUtils() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ /**
+ * @param <K> The {@link Comparable} key type
+ * @param <V> The associated entry value
+ * @return A {@link Comparator} for {@link java.util.Map.Entry}-ies that
+ * compares the key values
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public static <K extends Comparable<K>, V> Comparator<Map.Entry<K, V>> byKeyEntryComparator() {
+ return (Comparator) BY_KEY_COMPARATOR;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/NumberUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/NumberUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/NumberUtils.java
new file mode 100644
index 0000000..c8e1817
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/NumberUtils.java
@@ -0,0 +1,310 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.IntStream;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class NumberUtils {
+ /**
+ * A {@link List} of all the {@link Class} types used to represent the
+ * primitive numerical values
+ */
+ public static final List<Class<?>> NUMERIC_PRIMITIVE_CLASSES =
+ GenericUtils.unmodifiableList(
+ Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE);
+
+ /**
+ * A {@link List} containing all the pure powers of 2 for a {@code long}
+ * value. The value at index <I>n</I> is 2 to the power of <I>n</I>
+ */
+ public static final List<Long> POWERS_OF_TWO =
+ GenericUtils.unmodifiableList(IntStream.range(0, 64).mapToObj(i -> 1L << i));
+
+ private NumberUtils() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ public static boolean isPowerOf2(long value) {
+ for (Long l : POWERS_OF_TWO) {
+ if (value == l) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static long getNextPowerOf2(long value) {
+ long j = 1L;
+ while (j < value) {
+ j <<= 1;
+ }
+ return j;
+ }
+
+ public static int getNextPowerOf2(int value) {
+ int j = 1;
+ while (j < value) {
+ j <<= 1;
+ }
+ return j;
+ }
+
+ public static int hashCode(long... values) {
+ return Arrays.hashCode(values);
+ }
+
+ public static int hashCode(int... values) {
+ return Arrays.hashCode(values);
+ }
+
+ public static int hashCode(byte... values) {
+ return Arrays.hashCode(values);
+ }
+
+ public static int hashCode(byte[] a, int offset, int len) {
+ if (len == 0) {
+ return 0;
+ }
+
+ int result = 1;
+ for (int pos = offset, count = 0; count < len; pos++, count++) {
+ byte element = a[pos];
+ result = 31 * result + element;
+ }
+
+ return result;
+ }
+
+ public static int diffOffset(byte[] a1, int startPos1, byte[] a2, int startPos2, int len) {
+ for (int pos1 = startPos1, pos2 = startPos2, count = 0; count < len; pos1++, pos2++, count++) {
+ byte v1 = a1[pos1];
+ byte v2 = a2[pos2];
+ if (v1 != v2) {
+ return count;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * @param clazz The {@link Class} to examine - ignored if {@code null}
+ * @return If the class is a {@link Number} or one of the primitive numerical types
+ * @see #NUMERIC_PRIMITIVE_CLASSES
+ */
+ public static boolean isNumericClass(Class<?> clazz) {
+ if (clazz == null) {
+ return false;
+ }
+
+ // turns out that the primitive types are not assignable to Number
+ if (Number.class.isAssignableFrom(clazz)) {
+ return true;
+ }
+
+ return NUMERIC_PRIMITIVE_CLASSES.indexOf(clazz) >= 0;
+ }
+
+ /**
+ * Converts a {@link Number} into an {@link Integer} if not already such
+ *
+ * @param n The {@link Number} - ignored if {@code null}
+ * @return The equivalent {@link Integer} value
+ */
+ public static Integer toInteger(Number n) {
+ if (n == null) {
+ return null;
+ } else if (n instanceof Integer) {
+ return (Integer) n;
+ } else {
+ return n.intValue();
+ }
+ }
+
+ public static String join(CharSequence separator, long... values) {
+ if (NumberUtils.isEmpty(values)) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+ for (long v : values) {
+ if (sb.length() > 0) {
+ sb.append(separator);
+ }
+ sb.append(v);
+ }
+
+ return sb.toString();
+ }
+
+ public static String join(char separator, long... values) {
+ if (NumberUtils.isEmpty(values)) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+ for (long v : values) {
+ if (sb.length() > 0) {
+ sb.append(separator);
+ }
+ sb.append(v);
+ }
+
+ return sb.toString();
+ }
+
+ public static String join(CharSequence separator, boolean unsigned, byte... values) {
+ if (NumberUtils.isEmpty(values)) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+ for (byte v : values) {
+ if (sb.length() > 0) {
+ sb.append(separator);
+ }
+ sb.append(unsigned ? (v & 0xFF) : v);
+ }
+
+ return sb.toString();
+ }
+
+ public static String join(char separator, boolean unsigned, byte... values) {
+ if (NumberUtils.isEmpty(values)) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+ for (byte v : values) {
+ if (sb.length() > 0) {
+ sb.append(separator);
+ }
+ sb.append(unsigned ? (v & 0xFF) : v);
+ }
+
+ return sb.toString();
+ }
+
+ public static String join(CharSequence separator, int... values) {
+ if (NumberUtils.isEmpty(values)) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+ for (int v : values) {
+ if (sb.length() > 0) {
+ sb.append(separator);
+ }
+ sb.append(v);
+ }
+
+ return sb.toString();
+ }
+
+ public static String join(char separator, int... values) {
+ if (NumberUtils.isEmpty(values)) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
+ for (int v : values) {
+ if (sb.length() > 0) {
+ sb.append(separator);
+ }
+ sb.append(v);
+ }
+
+ return sb.toString();
+ }
+
+ public static boolean isEmpty(byte[] a) {
+ return NumberUtils.length(a) <= 0;
+ }
+
+ public static boolean isEmpty(int[] a) {
+ return NumberUtils.length(a) <= 0;
+ }
+
+ public static boolean isEmpty(long[] a) {
+ return NumberUtils.length(a) <= 0;
+ }
+
+ public static int length(byte... a) {
+ return a == null ? 0 : a.length;
+ }
+
+ public static int length(int... a) {
+ return a == null ? 0 : a.length;
+ }
+
+ public static int length(long... a) {
+ return a == null ? 0 : a.length;
+ }
+
+ public static List<Integer> asList(int... values) {
+ int len = length(values);
+ if (len <= 0) {
+ return Collections.emptyList();
+ }
+
+ List<Integer> l = new ArrayList<>(len);
+ for (int v : values) {
+ l.add(v);
+ }
+
+ return l;
+ }
+
+ /**
+ * Checks if optional sign and all others are '0'-'9'
+ * @param cs The {@link CharSequence} to check
+ * @return {@code true} if valid integer number
+ */
+ public static boolean isIntegerNumber(CharSequence cs) {
+ if (GenericUtils.isEmpty(cs)) {
+ return false;
+ }
+
+ for (int index = 0; index < cs.length(); index++) {
+ char c = cs.charAt(0);
+ if ((c >= '0') && (c <= '9')) {
+ continue;
+ }
+
+ if ((c == '+') || (c == '-')) {
+ if (index == 0) {
+ continue;
+ }
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java b/sshd-common/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java
new file mode 100644
index 0000000..23884a2
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.function.Supplier;
+
+/**
+ * A generic builder interface
+ *
+ * @param <T> Type of object being built
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface ObjectBuilder<T> extends Supplier<T> {
+ @Override
+ default T get() {
+ return build();
+ }
+
+ T build();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java
new file mode 100644
index 0000000..f0b2c7e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/OsUtils.java
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Operating system dependent utility methods.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class OsUtils {
+
+ /**
+ * Property that can be used to override the reported value from {@link #getCurrentUser()}.
+ * If not set then "user.name" system property is used
+ */
+ public static final String CURRENT_USER_OVERRIDE_PROP = "org.apache.sshd.currentUser";
+
+ /**
+ * Property that can be used to override the reported value from {@link #getJavaVersion()}.
+ * If not set then "java.version" system property is used
+ */
+ public static final String JAVA_VERSION_OVERRIDE_PROP = "org.apache.sshd.javaVersion";
+
+ /**
+ * Property that can be used to override the reported value from {@link #isWin32()}.
+ * If not set then "os.name" system property is used
+ */
+ public static final String OS_TYPE_OVERRIDE_PROP = "org.apache.sshd.osType";
+
+ public static final String WINDOWS_SHELL_COMMAND_NAME = "cmd.exe";
+ public static final String LINUX_SHELL_COMMAND_NAME = "/bin/sh";
+
+ public static final String ROOT_USER = "root";
+
+ public static final List<String> LINUX_COMMAND =
+ Collections.unmodifiableList(Arrays.asList(LINUX_SHELL_COMMAND_NAME, "-i", "-l"));
+ public static final List<String> WINDOWS_COMMAND =
+ Collections.unmodifiableList(Collections.singletonList(WINDOWS_SHELL_COMMAND_NAME));
+
+ private static final AtomicReference<String> CURRENT_USER_HOLDER = new AtomicReference<>(null);
+ private static final AtomicReference<VersionInfo> JAVA_VERSION_HOLDER = new AtomicReference<>(null);
+ private static final AtomicReference<Boolean> OS_TYPE_HOLDER = new AtomicReference<>(null);
+
+ private OsUtils() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+
+ /**
+ * @return true if the host is a UNIX system (and not Windows).
+ */
+ public static boolean isUNIX() {
+ return !isWin32();
+ }
+
+ /**
+ * @return true if the host is Windows (and not UNIX).
+ * @see #OS_TYPE_OVERRIDE_PROP
+ * @see #setWin32(Boolean)
+ */
+ public static boolean isWin32() {
+ Boolean typeValue;
+ synchronized (OS_TYPE_HOLDER) {
+ typeValue = OS_TYPE_HOLDER.get();
+ if (typeValue != null) { // is it the 1st time
+ return typeValue;
+ }
+
+ String value = System.getProperty(OS_TYPE_OVERRIDE_PROP, System.getProperty("os.name"));
+ typeValue = GenericUtils.trimToEmpty(value).toLowerCase().contains("windows");
+ OS_TYPE_HOLDER.set(typeValue);
+ }
+
+ return typeValue;
+ }
+
+ /**
+ * Can be used to enforce Win32 or Linux report from {@link #isWin32()} or {@link #isUNIX()}
+ * @param win32 The value to set - if {@code null} then O/S type is auto-detected
+ * @see #isWin32()
+ */
+ public static void setWin32(Boolean win32) {
+ synchronized (OS_TYPE_HOLDER) {
+ OS_TYPE_HOLDER.set(win32);
+ }
+ }
+
+ public static List<String> resolveDefaultInteractiveCommand() {
+ return resolveInteractiveCommand(isWin32());
+ }
+
+ public static List<String> resolveInteractiveCommand(boolean isWin32) {
+ if (isWin32) {
+ return WINDOWS_COMMAND;
+ } else {
+ return LINUX_COMMAND;
+ }
+ }
+
+ /**
+ * Get current user name
+ *
+ * @return Current user
+ * @see #CURRENT_USER_OVERRIDE_PROP
+ */
+ public static String getCurrentUser() {
+ String username = null;
+ synchronized (CURRENT_USER_HOLDER) {
+ username = CURRENT_USER_HOLDER.get();
+ if (username != null) { // have we already resolved it ?
+ return username;
+ }
+
+ username = getCanonicalUser(System.getProperty(CURRENT_USER_OVERRIDE_PROP, System.getProperty("user.name")));
+ ValidateUtils.checkNotNullAndNotEmpty(username, "No username available");
+ CURRENT_USER_HOLDER.set(username);
+ }
+
+ return username;
+ }
+
+ /**
+ * Remove {@code Windows} domain and/or group prefix as well as "(User);" suffix
+ *
+ * @param user The original username - ignored if {@code null}/empty
+ * @return The canonical user - unchanged if {@code Unix} O/S
+ */
+ public static String getCanonicalUser(String user) {
+ if (GenericUtils.isEmpty(user)) {
+ return user;
+ }
+
+ // Windows owner sometime has the domain and/or group prepended to it
+ if (isWin32()) {
+ int pos = user.lastIndexOf('\\');
+ if (pos > 0) {
+ user = user.substring(pos + 1);
+ }
+
+ pos = user.indexOf(' ');
+ if (pos > 0) {
+ user = user.substring(0, pos).trim();
+ }
+ }
+
+ return user;
+ }
+
+ /**
+ * Attempts to resolve canonical group name for {@code Windows}
+ *
+ * @param group The original group name - used if not {@code null}/empty
+ * @param user The owner name - sometimes it contains a group name
+ * @return The canonical group name
+ */
+ public static String resolveCanonicalGroup(String group, String user) {
+ if (isUNIX()) {
+ return group;
+ }
+
+ // we reach this code only for Windows
+ if (GenericUtils.isEmpty(group)) {
+ int pos = GenericUtils.isEmpty(user) ? -1 : user.lastIndexOf('\\');
+ return (pos > 0) ? user.substring(0, pos) : group;
+ }
+
+ int pos = group.indexOf(' ');
+ return (pos < 0) ? group : group.substring(0, pos).trim();
+ }
+
+ /**
+ * Can be used to programmatically set the username reported by {@link #getCurrentUser()}
+ * @param username The username to set - if {@code null} then {@link #CURRENT_USER_OVERRIDE_PROP}
+ * will be consulted
+ */
+ public static void setCurrentUser(String username) {
+ synchronized (CURRENT_USER_HOLDER) {
+ CURRENT_USER_HOLDER.set(username);
+ }
+ }
+
+ /**
+ * Resolves the reported Java version by consulting {@link #JAVA_VERSION_OVERRIDE_PROP}.
+ * If not set, then "java.version" property is used
+ * @return The resolved {@link VersionInfo} - never {@code null}
+ * @see #setJavaVersion(VersionInfo)
+ */
+ public static VersionInfo getJavaVersion() {
+ VersionInfo version;
+ synchronized (JAVA_VERSION_HOLDER) {
+ version = JAVA_VERSION_HOLDER.get();
+ if (version != null) { // first time ?
+ return version;
+ }
+
+ String value = System.getProperty(JAVA_VERSION_OVERRIDE_PROP, System.getProperty("java.version"));
+ // e.g.: 1.7.5_30
+ value = ValidateUtils.checkNotNullAndNotEmpty(value, "No configured Java version value").replace('_', '.');
+ // clean up any non-digits - in case something like 1.6.8_25-b323
+ for (int index = 0; index < value.length(); index++) {
+ char ch = value.charAt(index);
+ if ((ch == '.') || ((ch >= '0') && (ch <= '9'))) {
+ continue;
+ }
+
+ value = value.substring(0, index);
+ break;
+ }
+
+ version = ValidateUtils.checkNotNull(VersionInfo.parse(value), "No version parsed for %s", value);
+ JAVA_VERSION_HOLDER.set(version);
+ }
+
+ return version;
+ }
+
+ /**
+ * Set programmatically the reported Java version
+ * @param version The version - if {@code null} then it will be automatically resolved
+ */
+ public static void setJavaVersion(VersionInfo version) {
+ synchronized (JAVA_VERSION_HOLDER) {
+ JAVA_VERSION_HOLDER.set(version);
+ }
+ }
+
+ /**
+ * @param path The original path
+ * @return A path that can be compared with another one where case
+ * sensitivity of the underlying O/S has been taken into account -
+ * never {@code null}
+ */
+ public static String getComparablePath(String path) {
+ String p = (path == null) ? "" : path;
+ return isWin32() ? p.toLowerCase() : p;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/Readable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/Readable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/Readable.java
new file mode 100644
index 0000000..3075eb3
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/Readable.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Readable {
+
+ int available();
+
+ void getRawBytes(byte[] data, int offset, int len);
+
+ /**
+ * Wrap a {@link ByteBuffer} as a {@link Readable} instance
+ *
+ * @param buffer The {@link ByteBuffer} to wrap - never {@code null}
+ * @return The {@link Readable} wrapper
+ */
+ static Readable readable(ByteBuffer buffer) {
+ Objects.requireNonNull(buffer, "No buffer to wrap");
+ return new Readable() {
+ @Override
+ public int available() {
+ return buffer.remaining();
+ }
+
+ @Override
+ public void getRawBytes(byte[] data, int offset, int len) {
+ buffer.get(data, offset, len);
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
new file mode 100644
index 0000000..84a84f9
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.lang.reflect.Field;
+import java.util.Collection;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class ReflectionUtils {
+ public static final Function<Field, String> FIELD_NAME_EXTRACTOR = f -> (f == null) ? null : f.getName();
+
+ private ReflectionUtils() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ public static Collection<Field> getMatchingFields(Class<?> clazz, Predicate<? super Field> acceptor) {
+ return GenericUtils.selectMatchingMembers(acceptor, clazz.getFields());
+ }
+
+ public static Collection<Field> getMatchingDeclaredFields(Class<?> clazz, Predicate<? super Field> acceptor) {
+ return GenericUtils.selectMatchingMembers(acceptor, clazz.getDeclaredFields());
+ }
+
+ public static boolean isClassAvailable(ClassLoader cl, String className) {
+ try {
+ cl.loadClass(className);
+ return true;
+ } catch (Throwable ignored) {
+ return false;
+ }
+ }
+}
[07/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
deleted file mode 100644
index a83b9de..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
+++ /dev/null
@@ -1,173 +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.util;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.NoSuchElementException;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class GenericUtilsTest extends BaseTestSupport {
- public GenericUtilsTest() {
- super();
- }
-
- @Test
- public void testSplitAndJoin() {
- List<String> expected = Collections.unmodifiableList(
- Arrays.asList(getClass().getPackage().getName().replace('.', '/'), getClass().getSimpleName(), getCurrentTestName()));
-
- // NOTE: we also test characters that have meaning in String.split(...) as regex ones
- for (char ch : new char[]{',', '.', '*', '?'}) {
- String sep = String.valueOf(ch);
- String s = GenericUtils.join(expected, sep);
- String[] actual = GenericUtils.split(s, ch);
- assertEquals("Mismatched split length for separator=" + sep, expected.size(), GenericUtils.length((Object[]) actual));
-
- for (int index = 0; index < actual.length; index++) {
- String e = expected.get(index);
- String a = actual[index];
- if (!e.endsWith(a)) {
- fail("Mismatched value at index=" + index + " for separator=" + sep + ": expected=" + e + ", actual=" + a);
- }
- }
- }
- }
-
- @Test
- public void testStripQuotes() {
- String expected = getCurrentTestName();
- assertSame("Unexpected un-quoted stripping", expected, GenericUtils.stripQuotes(expected));
-
- StringBuilder sb = new StringBuilder(2 + expected.length()).append('|').append(expected).append('|');
- for (int index = 0; index < GenericUtils.QUOTES.length(); index++) {
- char delim = GenericUtils.QUOTES.charAt(index);
- sb.setCharAt(0, delim);
- sb.setCharAt(sb.length() - 1, delim);
-
- CharSequence actual = GenericUtils.stripQuotes(sb);
- assertEquals("Mismatched result for delim (" + delim + ")", expected, actual.toString());
- }
- }
-
- @Test
- public void testStripOnlyFirstLayerQuotes() {
- StringBuilder sb = new StringBuilder().append("||").append(getCurrentTestName()).append("||");
- char[] delims = {'\'', '"', '"', '\''};
- for (int index = 0; index < delims.length; index += 2) {
- char topDelim = delims[index];
- char innerDelim = delims[index + 1];
- sb.setCharAt(0, topDelim);
- sb.setCharAt(1, innerDelim);
- sb.setCharAt(sb.length() - 2, innerDelim);
- sb.setCharAt(sb.length() - 1, topDelim);
-
- CharSequence expected = sb.subSequence(1, sb.length() - 1);
- CharSequence actual = GenericUtils.stripQuotes(sb);
- assertEquals("Mismatched result for delim (" + topDelim + "/" + innerDelim + ")", expected.toString(), actual.toString());
- }
- }
-
- @Test
- public void testStripDelimiters() {
- String expected = getCurrentTestName();
- final char delim = '|';
- assertSame("Unexpected un-delimited stripping", expected, GenericUtils.stripDelimiters(expected, delim));
-
- CharSequence actual = GenericUtils.stripDelimiters(
- new StringBuilder(2 + expected.length()).append(delim).append(expected).append(delim), delim);
- assertEquals("Mismatched stripped values", expected, actual.toString());
- }
-
- @Test
- public void testStripDelimitersOnlyIfOnBothEnds() {
- final char delim = '$';
- StringBuilder expected = new StringBuilder().append(delim).append(getCurrentTestName()).append(delim);
- for (int index : new int[]{0, expected.length() - 1}) {
- // restore original delimiters
- expected.setCharAt(0, delim);
- expected.setCharAt(expected.length() - 1, delim);
- // trash one end
- expected.setCharAt(index, (char) (delim + 1));
-
- assertSame("Mismatched result for delim at index=" + index, expected, GenericUtils.stripDelimiters(expected, delim));
- }
- }
-
- @Test
- public void testAccumulateExceptionOnNullValues() {
- assertNull("Unexpected null/null result", GenericUtils.accumulateException(null, null));
-
- Throwable expected = new NoSuchMethodException(getClass().getName() + "#" + getCurrentTestName());
- assertSame("Mismatched null/extra result", expected, GenericUtils.accumulateException(null, expected));
- assertSame("Mismatched current/null result", expected, GenericUtils.accumulateException(expected, null));
- }
-
- @Test
- public void testAccumulateExceptionOnExistingCurrent() {
- RuntimeException[] expected = new RuntimeException[]{
- new IllegalArgumentException(getCurrentTestName()),
- new ClassCastException(getClass().getName()),
- new NoSuchElementException(getClass().getPackage().getName())
- };
- RuntimeException current = new UnsupportedOperationException("top");
- for (RuntimeException extra : expected) {
- RuntimeException actual = GenericUtils.accumulateException(current, extra);
- assertSame("Mismatched returned actual exception", current, actual);
- }
-
- Throwable[] actual = current.getSuppressed();
- assertArrayEquals("Suppressed", expected, actual);
- }
-
- @Test
- public void testNullOrEmptyCharArrayComparison() {
- char[][] values = new char[][]{null, GenericUtils.EMPTY_CHAR_ARRAY};
- for (char[] c1 : values) {
- for (char[] c2 : values) {
- assertEquals(((c1 == null) ? "null" : "empty") + " vs. " + ((c2 == null) ? "null" : "empty"), 0, GenericUtils.compare(c1, c2));
- }
- }
- }
-
- @Test
- public void testCharArrayComparison() {
- String s1 = getClass().getSimpleName();
- char[] c1 = s1.toCharArray();
- assertEquals("Same value equality", 0, GenericUtils.compare(c1, s1.toCharArray()));
-
- String s2 = getCurrentTestName();
- char[] c2 = s2.toCharArray();
- assertEquals("s1 vs. s2", Integer.signum(s1.compareTo(s2)), Integer.signum(GenericUtils.compare(c1, c2)));
- assertEquals("s2 vs. s1", Integer.signum(s2.compareTo(s1)), Integer.signum(GenericUtils.compare(c2, c1)));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java
deleted file mode 100644
index 941190b..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java
+++ /dev/null
@@ -1,154 +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.util;
-
-import java.util.Random;
-import java.util.function.IntUnaryOperator;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class Int2IntFunctionTest extends BaseTestSupport {
- public Int2IntFunctionTest() {
- super();
- }
-
- @Test
- public void testAdd() {
- int factor = Byte.SIZE;
- IntUnaryOperator func = Int2IntFunction.add(factor);
- for (int index = 1, sum = 0; index <= Byte.SIZE; index++) {
- sum = func.applyAsInt(sum);
- assertEquals(factor * index, sum);
- }
- }
-
- @Test
- public void testAddIdentity() {
- IntUnaryOperator func = Int2IntFunction.add(0);
- Random rnd = new Random(System.nanoTime());
- for (int index = 1; index <= Byte.SIZE; index++) {
- int expected = rnd.nextInt();
- int actual = func.applyAsInt(expected);
- assertEquals(expected, actual);
- }
- }
-
- @Test
- public void testSub() {
- int factor = Byte.SIZE;
- IntUnaryOperator func = Int2IntFunction.sub(factor);
- for (int index = 1, sum = 0; index <= Byte.SIZE; index++) {
- sum = func.applyAsInt(sum);
- assertEquals(factor * index * -1, sum);
- }
- }
-
- @Test
- public void testSubIdentity() {
- IntUnaryOperator func = Int2IntFunction.sub(0);
- Random rnd = new Random(System.nanoTime());
- for (int index = 1; index <= Byte.SIZE; index++) {
- int expected = rnd.nextInt();
- int actual = func.applyAsInt(expected);
- assertEquals(expected, actual);
- }
- }
-
- @Test
- public void testMul() {
- int factor = 2;
- IntUnaryOperator func = Int2IntFunction.mul(factor);
- for (int index = 1, mul = 1, expected = factor; index <= Byte.SIZE; index++, expected *= factor) {
- mul = func.applyAsInt(mul);
- assertEquals(expected, mul);
- }
- }
-
- @Test
- public void testMulIdentity() {
- IntUnaryOperator func = Int2IntFunction.mul(1);
- Random rnd = new Random(System.nanoTime());
- for (int index = 1; index <= Byte.SIZE; index++) {
- int expected = rnd.nextInt();
- int actual = func.applyAsInt(expected);
- assertEquals(expected, actual);
- }
- }
-
- @Test
- public void testMulZero() {
- IntUnaryOperator func = Int2IntFunction.mul(0);
- Random rnd = new Random(System.nanoTime());
- for (int index = 1; index <= Byte.SIZE; index++) {
- int value = rnd.nextInt();
- int actual = func.applyAsInt(value);
- assertEquals(Integer.toString(value), 0, actual);
- }
- }
-
- @Test
- public void testConstant() {
- int expected = 377347;
- IntUnaryOperator func = Int2IntFunction.constant(expected);
- Random rnd = new Random(System.nanoTime());
- for (int index = 1; index <= Byte.SIZE; index++) {
- int value = rnd.nextInt();
- int actual = func.applyAsInt(value);
- assertEquals(Integer.toString(value), expected, actual);
- }
- }
-
- @Test
- public void testDiv() {
- int factor = 2;
- IntUnaryOperator func = Int2IntFunction.div(factor);
- for (int index = 1, quot = 65536, expected = quot / factor; index <= Byte.SIZE; index++, expected /= factor) {
- quot = func.applyAsInt(quot);
- assertEquals(expected, quot);
- }
- }
-
- @Test
- public void testDivIdentity() {
- IntUnaryOperator func = Int2IntFunction.div(1);
- Random rnd = new Random(System.nanoTime());
- for (int index = 1; index <= Byte.SIZE; index++) {
- int expected = rnd.nextInt();
- int actual = func.applyAsInt(expected);
- assertEquals(expected, actual);
- }
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testDivZeroFactor() {
- IntUnaryOperator func = Int2IntFunction.div(0);
- fail("Unexpected success: " + func);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
deleted file mode 100644
index 8109b55..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
+++ /dev/null
@@ -1,77 +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.util;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class NumberUtilsTest extends BaseTestSupport {
- public NumberUtilsTest() {
- super();
- }
-
- @Test
- public void testPowersOf2List() {
- assertEquals("Mismatched values size for " + NumberUtils.POWERS_OF_TWO, Long.SIZE, GenericUtils.size(NumberUtils.POWERS_OF_TWO));
- long expected = 1L;
- for (int index = 0; index < Long.SIZE; index++, expected <<= 1) {
- Long actual = NumberUtils.POWERS_OF_TWO.get(index);
- assertEquals("Mismatched value at index=" + index, Long.toHexString(expected), Long.toHexString(actual));
- }
- }
-
- @Test
- public void testNextPowerOf2() {
- for (Long v : NumberUtils.POWERS_OF_TWO) {
- long expected = v;
- if (expected > 2L) {
- assertEquals("Mismatched lower bound value", expected, NumberUtils.getNextPowerOf2(expected - 1L));
- }
-
- if (expected > 0L) { // avoid the negative value
- assertEquals("Mismatched exact value", expected, NumberUtils.getNextPowerOf2(expected));
- }
- }
- }
-
- @Test
- public void testToInteger() {
- assertNull("Unexpected null value", NumberUtils.toInteger(null));
- for (Number n : new Number[]{
- Byte.valueOf(Byte.MAX_VALUE), Short.valueOf(Short.MIN_VALUE),
- Integer.valueOf(Short.MAX_VALUE), Long.valueOf(82007160L)}) {
- Integer i = NumberUtils.toInteger(n);
- if (n instanceof Integer) {
- assertSame("Unexpected conversion", n, i);
- } else {
- assertEquals("Mismatched values", n.intValue(), i.intValue());
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
deleted file mode 100644
index 62081af..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
+++ /dev/null
@@ -1,135 +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.util;
-
-import java.util.Objects;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class OsUtilsTest extends BaseTestSupport {
- public OsUtilsTest() {
- super();
- }
-
- @Test
- public void testSetOsTypeByProperty() {
- try {
- for (String osType : new String[]{"Some-Windows", "Some-Linux"}) {
- OsUtils.setWin32(null); // force re-detection
-
- try {
- boolean expected = osType.contains("Windows");
- System.setProperty(OsUtils.OS_TYPE_OVERRIDE_PROP, osType);
- boolean actual = OsUtils.isWin32();
- assertEquals(osType, expected, actual);
- } finally {
- System.clearProperty(OsUtils.OS_TYPE_OVERRIDE_PROP);
- }
- }
- } finally {
- OsUtils.setWin32(null); // force re-detection
- }
- }
-
- @Test
- public void testSetOsTypeProgrammatically() {
- try {
- for (boolean expected : new boolean[]{true, false}) {
- OsUtils.setWin32(expected); // force value
- assertEquals("Mismatched detection value", expected, OsUtils.isWin32());
- }
- } finally {
- OsUtils.setWin32(null); // force re-detection
- }
- }
-
- @Test
- public void testSetCurrentUserByProperty() {
- try {
- for (String expected : new String[]{getClass().getSimpleName(), getCurrentTestName()}) {
- OsUtils.setCurrentUser(null); // force re-detection
-
- try {
- System.setProperty(OsUtils.CURRENT_USER_OVERRIDE_PROP, expected);
- String actual = OsUtils.getCurrentUser();
- assertEquals("Mismatched reported current user", expected, actual);
- } finally {
- System.clearProperty(OsUtils.CURRENT_USER_OVERRIDE_PROP);
- }
- }
- } finally {
- OsUtils.setCurrentUser(null); // force re-detection
- }
- }
-
- @Test
- public void testSetCurrentUserProgrammatically() {
- try {
- for (String expected : new String[]{getClass().getSimpleName(), getCurrentTestName()}) {
- OsUtils.setCurrentUser(expected); // force value
- assertEquals("Mismatched detection value", expected, OsUtils.getCurrentUser());
- }
- } finally {
- OsUtils.setCurrentUser(null); // force re-detection
- }
- }
-
- @Test
- public void testSetJavaVersionByProperty() {
- try {
- for (String value : new String[]{"7.3.6_5", "37.77.34_7-" + getCurrentTestName()}) {
- OsUtils.setJavaVersion(null); // force re-detection
-
- try {
- System.setProperty(OsUtils.JAVA_VERSION_OVERRIDE_PROP, value);
- String expected = value.replace('_', '.');
- String actual = Objects.toString(OsUtils.getJavaVersion(), null);
- assertTrue("Mismatched reported version value: " + actual, expected.startsWith(actual));
- } finally {
- System.clearProperty(OsUtils.JAVA_VERSION_OVERRIDE_PROP);
- }
- }
- } finally {
- OsUtils.setJavaVersion(null); // force re-detection
- }
- }
-
- @Test
- public void testSetJavaVersionProgrammatically() {
- try {
- for (VersionInfo expected : new VersionInfo[]{VersionInfo.parse("7.3.6.5"), VersionInfo.parse("37.77.34.7")}) {
- OsUtils.setJavaVersion(expected); // force value
- assertEquals("Mismatched detection value", expected, OsUtils.getJavaVersion());
- }
- } finally {
- OsUtils.setJavaVersion(null); // force re-detection
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java
deleted file mode 100644
index c4e521c..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java
+++ /dev/null
@@ -1,232 +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.util;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-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.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.sshd.common.cipher.BuiltinCiphers;
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceLoader;
-import org.apache.sshd.common.keyprovider.AbstractResourceKeyPairProvider;
-import org.apache.sshd.common.keyprovider.ClassLoadableResourceKeyPairProvider;
-import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityProviderRegistrar;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.AfterClass;
-import org.junit.Assume;
-import org.junit.BeforeClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-@SuppressWarnings("checkstyle:MethodCount")
-public class SecurityUtilsTest extends BaseTestSupport {
- public static final String BC_NAMED_USAGE_PROP =
- SecurityProviderRegistrar.CONFIG_PROP_BASE
- + "." + SecurityUtils.BOUNCY_CASTLE
- + "." + SecurityProviderRegistrar.NAMED_PROVIDER_PROPERTY;
-
- private static final String DEFAULT_PASSWORD = "super secret passphrase";
- private static final FilePasswordProvider TEST_PASSWORD_PROVIDER = file -> DEFAULT_PASSWORD;
-
- public SecurityUtilsTest() {
- super();
- }
-
- // NOTE: Using the BouncyCastle provider instead of the name does not work as expected so we take no chances
- @BeforeClass
- public static void useNamedBouncyCastleProvider() {
- System.setProperty(BC_NAMED_USAGE_PROP, Boolean.TRUE.toString());
- }
-
- @AfterClass
- public static void unsetBouncyCastleProviderUsagePreference() {
- System.clearProperty(BC_NAMED_USAGE_PROP);
- }
-
- @Test
- public void testLoadEncryptedDESPrivateKey() throws Exception {
- testLoadEncryptedRSAPrivateKey("DES-EDE3");
- }
-
- @Test
- public void testLoadEncryptedAESPrivateKey() {
- for (BuiltinCiphers c : new BuiltinCiphers[]{
- BuiltinCiphers.aes128cbc, BuiltinCiphers.aes192cbc, BuiltinCiphers.aes256cbc
- }) {
- if (!c.isSupported()) {
- System.out.println("Skip unsupported encryption scheme: " + c.getName());
- continue;
- }
-
- try {
- testLoadEncryptedRSAPrivateKey("AES-" + c.getKeySize());
- } catch (Exception e) {
- fail("Failed (" + e.getClass().getSimpleName() + " to load key for " + c.getName() + ": " + e.getMessage());
- }
- }
- }
-
- private KeyPair testLoadEncryptedRSAPrivateKey(String algorithm) throws IOException, GeneralSecurityException {
- return testLoadRSAPrivateKey(DEFAULT_PASSWORD.replace(' ', '-') + "-RSA-" + algorithm.toUpperCase() + "-key");
- }
-
- @Test
- public void testLoadUnencryptedRSAPrivateKey() throws Exception {
- testLoadRSAPrivateKey(getClass().getSimpleName() + "-RSA-KeyPair");
- }
-
- @Test
- public void testLoadUnencryptedDSSPrivateKey() throws Exception {
- testLoadDSSPrivateKey(getClass().getSimpleName() + "-DSA-KeyPair");
- }
-
- private KeyPair testLoadDSSPrivateKey(String name) throws Exception {
- return testLoadPrivateKey(name, DSAPublicKey.class, DSAPrivateKey.class);
- }
-
- @Test
- public void testLoadUnencryptedECPrivateKey() throws Exception {
- Assume.assumeTrue("EC not supported", SecurityUtils.isECCSupported());
- for (ECCurves c : ECCurves.VALUES) {
- if (!c.isSupported()) {
- System.out.println("Skip unsupported curve: " + c.getName());
- continue;
- }
-
- testLoadECPrivateKey(getClass().getSimpleName() + "-EC-" + c.getKeySize() + "-KeyPair");
- }
- }
-
- private KeyPair testLoadECPrivateKey(String name) throws IOException, GeneralSecurityException {
- return testLoadPrivateKey(name, ECPublicKey.class, ECPrivateKey.class);
- }
-
- private KeyPair testLoadRSAPrivateKey(String name) throws IOException, GeneralSecurityException {
- return testLoadPrivateKey(name, RSAPublicKey.class, RSAPrivateKey.class);
- }
-
- private KeyPair testLoadPrivateKey(String name, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType)
- throws IOException, GeneralSecurityException {
- Path folder = getTestResourcesFolder();
- Path file = folder.resolve(name);
- KeyPair kpFile = testLoadPrivateKeyFile(file, pubType, prvType);
- if (SecurityUtils.isBouncyCastleRegistered()) {
- KeyPairResourceLoader bcLoader = SecurityUtils.getBouncycastleKeyPairResourceParser();
- Collection<KeyPair> kpList = bcLoader.loadKeyPairs(file, TEST_PASSWORD_PROVIDER);
- assertEquals(name + ": Mismatched loaded BouncyCastle keys count", 1, GenericUtils.size(kpList));
-
- KeyPair kpBC = kpList.iterator().next();
- assertTrue(name + ": Mismatched BouncyCastle vs. file values", KeyUtils.compareKeyPairs(kpFile, kpBC));
- }
-
- Class<?> clazz = getClass();
- Package pkg = clazz.getPackage();
- KeyPair kpResource = testLoadPrivateKeyResource(pkg.getName().replace('.', '/') + "/" + name, pubType, prvType);
- assertTrue(name + ": Mismatched key file vs. resource values", KeyUtils.compareKeyPairs(kpFile, kpResource));
- return kpResource;
- }
-
- private static KeyPair testLoadPrivateKeyResource(String name, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
- return testLoadPrivateKey(name, new ClassLoadableResourceKeyPairProvider(name), pubType, prvType);
- }
-
- private static KeyPair testLoadPrivateKeyFile(Path file, Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
- return testLoadPrivateKey(file.toString(), new FileKeyPairProvider(file), pubType, prvType);
- }
-
- private static KeyPair testLoadPrivateKey(String resourceKey, AbstractResourceKeyPairProvider<?> provider,
- Class<? extends PublicKey> pubType, Class<? extends PrivateKey> prvType) {
- provider.setPasswordFinder(TEST_PASSWORD_PROVIDER);
- Iterable<KeyPair> iterator = provider.loadKeys();
- List<KeyPair> pairs = new ArrayList<>();
- for (KeyPair kp : iterator) {
- pairs.add(kp);
- }
-
- assertEquals("Mismatched loaded pairs count for " + resourceKey, 1, pairs.size());
-
- KeyPair kp = pairs.get(0);
- PublicKey pub = kp.getPublic();
- assertNotNull("No public key extracted", pub);
- assertTrue("Not an " + pubType.getSimpleName() + " public key for " + resourceKey, pubType.isAssignableFrom(pub.getClass()));
-
- PrivateKey prv = kp.getPrivate();
- assertNotNull("No private key extracted", prv);
- assertTrue("Not an " + prvType.getSimpleName() + " private key for " + resourceKey, prvType.isAssignableFrom(prv.getClass()));
-
- return kp;
- }
-
- @Test
- public void testSetMaxDHGroupExchangeKeySizeByProperty() {
- try {
- for (int expected = SecurityUtils.MIN_DHGEX_KEY_SIZE; expected <= SecurityUtils.MAX_DHGEX_KEY_SIZE; expected += 1024) {
- SecurityUtils.setMaxDHGroupExchangeKeySize(0); // force detection
- try {
- System.setProperty(SecurityUtils.MAX_DHGEX_KEY_SIZE_PROP, Integer.toString(expected));
- assertTrue("DH group not supported for key size=" + expected, SecurityUtils.isDHGroupExchangeSupported());
- assertEquals("Mismatched values", expected, SecurityUtils.getMaxDHGroupExchangeKeySize());
- } finally {
- System.clearProperty(SecurityUtils.MAX_DHGEX_KEY_SIZE_PROP);
- }
- }
- } finally {
- SecurityUtils.setMaxDHGroupExchangeKeySize(0); // force detection
- }
- }
-
- @Test
- public void testSetMaxDHGroupExchangeKeySizeProgrammatically() {
- try {
- for (int expected = SecurityUtils.MIN_DHGEX_KEY_SIZE; expected <= SecurityUtils.MAX_DHGEX_KEY_SIZE; expected += 1024) {
- SecurityUtils.setMaxDHGroupExchangeKeySize(expected);
- assertTrue("DH group not supported for key size=" + expected, SecurityUtils.isDHGroupExchangeSupported());
- assertEquals("Mismatched values", expected, SecurityUtils.getMaxDHGroupExchangeKeySize());
- }
- } finally {
- SecurityUtils.setMaxDHGroupExchangeKeySize(0); // force detection
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java
deleted file mode 100644
index e13fa9b..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java
+++ /dev/null
@@ -1,147 +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.util;
-
-import java.io.File;
-import java.util.Random;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class SelectorUtilsTest extends BaseTestSupport {
- public SelectorUtilsTest() {
- super();
- }
-
- @Test
- public void testApplyLinuxSeparatorSlashifyRules() {
- testApplySlashifyRules('/');
- }
-
- @Test
- public void testApplyWindowsSeparatorSlashifyRules() {
- testApplySlashifyRules('\\');
- }
-
- private void testApplySlashifyRules(char slash) {
- for (String expected : new String[]{
- null, "", getCurrentTestName(),
- getClass().getSimpleName() + Character.toString(slash) + getCurrentTestName(),
- Character.toString(slash) + getClass().getSimpleName(),
- Character.toString(slash) + getClass().getSimpleName() + Character.toString(slash) + getCurrentTestName()
- }) {
- String actual = SelectorUtils.applySlashifyRules(expected, slash);
- assertSame("Mismatched results for '" + expected + "'", expected, actual);
- }
-
- String[] comps = {getClass().getSimpleName(), getCurrentTestName()};
- Random rnd = new Random(System.nanoTime());
- StringBuilder sb = new StringBuilder(Byte.MAX_VALUE);
- for (int index = 0; index < Long.SIZE; index++) {
- if (sb.length() > 0) {
- sb.setLength(0); // start from scratch
- }
-
- boolean prepend = rnd.nextBoolean();
- if (prepend) {
- slashify(sb, rnd, slash);
- }
-
- sb.append(comps[0]);
- for (int j = 1; j < comps.length; j++) {
- slashify(sb, rnd, slash);
- sb.append(comps[j]);
- }
-
- boolean append = rnd.nextBoolean();
- if (append) {
- slashify(sb, rnd, slash);
- }
-
- String path = sb.toString();
- sb.setLength(0);
- if (prepend) {
- sb.append(slash);
- }
-
- sb.append(comps[0]);
- for (int j = 1; j < comps.length; j++) {
- sb.append(slash).append(comps[j]);
- }
-
- if (append) {
- sb.append(slash).append('.');
- }
-
- String expected = sb.toString();
- String actual = SelectorUtils.applySlashifyRules(path, slash);
- assertEquals("Mismatched results for path=" + path, expected, actual);
- }
- }
-
- private static int slashify(StringBuilder sb, Random rnd, char slash) {
- int slashes = 1 /* at least one slash */ + rnd.nextInt(Byte.SIZE);
- for (int k = 0; k < slashes; k++) {
- sb.append(slash);
- }
-
- return slashes;
- }
-
- @Test
- public void testTranslateToFileSystemPath() {
- String path = getClass().getPackage().getName().replace('.', File.separatorChar)
- + File.separator + getClass().getSimpleName()
- + File.separator + getCurrentTestName();
- for (String expected : new String[] {null, "", path}) {
- String actual = SelectorUtils.translateToFileSystemPath(expected, File.separator, File.separator);
- assertSame("Mismatched instance for translated result", expected, actual);
- }
-
- for (String fsSeparator : new String[] {String.valueOf('.'), "##"}) {
- String expected = path.replace(File.separator, fsSeparator);
- String actual = SelectorUtils.translateToFileSystemPath(path, File.separator, fsSeparator);
- assertEquals("Mismatched translation result for separator='" + fsSeparator + "'", expected, actual);
-
- actual = SelectorUtils.translateToFileSystemPath(actual, fsSeparator, File.separator);
- assertEquals("Mismatched translation revert for separator='" + fsSeparator + "'", path, actual);
- }
- }
-
- @Test
- public void testAbsoluteWindowsPathTranslation() {
- Assume.assumeTrue("Not tested on Windows", OsUtils.isWin32());
- String expected = detectTargetFolder().toString();
- for (String prefix : new String[]{"", "/"}) {
- String actual = SelectorUtils.translateToLocalPath(prefix + expected.replace('/', File.separatorChar));
- assertEquals("Mismatched result for prefix='" + prefix + "'", expected, actual);
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java
deleted file mode 100644
index 50fda34..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java
+++ /dev/null
@@ -1,72 +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.util;
-
-import java.util.Collection;
-
-import org.apache.sshd.common.util.threads.CloseableExecutorService;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class ThreadUtilsTest extends BaseTestSupport {
- public ThreadUtilsTest() {
- super();
- }
-
- @Test
- public void testProtectExecutorServiceShutdown() {
- for (boolean shutdownOnExit : new boolean[]{true, false}) {
- assertNull("Unexpected instance for shutdown=" + shutdownOnExit, ThreadUtils.protectExecutorServiceShutdown(null, shutdownOnExit));
- }
-
- CloseableExecutorService service = ThreadUtils.newSingleThreadExecutor("pool");
- try {
- assertSame("Unexpected wrapped instance", service, ThreadUtils.protectExecutorServiceShutdown(service, true));
-
- CloseableExecutorService wrapped = ThreadUtils.protectExecutorServiceShutdown(service, false);
- try {
- assertNotSame("No wrapping occurred", service, wrapped);
-
- wrapped.shutdown();
- assertTrue("Wrapped service not shutdown", wrapped.isShutdown());
- assertFalse("Protected service is shutdown", service.isShutdown());
-
- Collection<?> running = wrapped.shutdownNow();
- assertTrue("Non-empty runners list", running.isEmpty());
- assertTrue("Wrapped service not shutdownNow", wrapped.isShutdown());
- assertFalse("Protected service is shutdownNow", service.isShutdown());
- } finally {
- wrapped.shutdownNow(); // just in case
- }
- } finally {
- service.shutdownNow(); // just in case...
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java
deleted file mode 100644
index 80c2e7f..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java
+++ /dev/null
@@ -1,44 +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.util;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class ValidateUtilsTest extends BaseTestSupport {
- public ValidateUtilsTest() {
- super();
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void checkNotNull() {
- ValidateUtils.checkNotNull(getClass(), getCurrentTestName());
- ValidateUtils.checkNotNull(null, getCurrentTestName());
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java
deleted file mode 100644
index 3b742a9..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java
+++ /dev/null
@@ -1,52 +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.util;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class VersionInfoTest extends BaseTestSupport {
- public VersionInfoTest() {
- super();
- }
-
- @Test
- public void testLessThan4Components() {
- VersionInfo expected = new VersionInfo(73, 65);
- VersionInfo actual = VersionInfo.parse(NumberUtils.join('.', expected.getMajorVersion(), expected.getMinorVersion()));
- assertEquals("Mismatched result", expected, actual);
- }
-
- @Test
- public void testMoreThan4Components() {
- VersionInfo expected = new VersionInfo(7, 3, 6, 5);
- VersionInfo actual = VersionInfo.parse(expected.toString() + ".3.7.7.7.3.4.7");
- assertEquals("Mismatched result", expected, actual);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
deleted file mode 100644
index be3f796..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
+++ /dev/null
@@ -1,103 +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.util.buffer;
-
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.nio.charset.StandardCharsets;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class BufferTest extends BaseTestSupport {
- public BufferTest() {
- super();
- }
-
- @Test
- public void testGetLong() throws Exception {
- long expected = 1234567890123456789L;
-
- try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
- try (DataOutputStream ds = new DataOutputStream(stream)) {
- ds.writeLong(expected);
- }
-
- Buffer buffer = new ByteArrayBuffer(stream.toByteArray());
- assertEquals("Mismatched recovered value", expected, buffer.getLong());
- }
- }
-
- @Test
- public void testPutCharsWithNullOrEmptyValue() {
- Buffer buffer = new ByteArrayBuffer(Integer.SIZE);
- for (char[] chars : new char[][]{null, GenericUtils.EMPTY_CHAR_ARRAY}) {
- buffer.putChars(chars);
-
- String value = buffer.getString();
- assertEquals("Mismatched value for " + ((chars == null) ? "null" : "empty") + " characters", "", value);
- }
- }
-
- @Test
- public void testPutCharsOnNonEmptyValue() {
- String expected = getCurrentTestName();
- Buffer buffer = new ByteArrayBuffer(expected.length() + Byte.SIZE);
- buffer.putChars(expected.toCharArray());
-
- String actual = buffer.getString();
- assertEquals("Mismatched recovered values", expected, actual);
- }
-
- @Test
- public void testPutAndWipeChars() {
- String expected = getCurrentTestName();
- char[] chars = expected.toCharArray();
- Buffer buffer = new ByteArrayBuffer(chars.length + Byte.SIZE);
- buffer.putAndWipeChars(chars);
-
- String actual = buffer.getString();
- assertEquals("Mismatched recovered values", expected, actual);
-
- for (int index = 0; index < chars.length; index++) {
- assertEquals("Character not wiped at index=" + index, 0, chars[index]);
- }
- }
-
- @Test
- public void testPutAndWipeBytes() {
- String expected = getCurrentTestName();
- byte[] bytes = expected.getBytes(StandardCharsets.UTF_8);
- Buffer buffer = new ByteArrayBuffer(bytes.length + Byte.SIZE);
- buffer.putAndWipeBytes(bytes);
- String actual = buffer.getString();
- assertEquals("Mismatched recovered values", expected, actual);
-
- for (int index = 0; index < bytes.length; index++) {
- assertEquals("Value not wiped at index=" + index, 0, bytes[index]);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
deleted file mode 100644
index b106bc6..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
+++ /dev/null
@@ -1,73 +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.util.buffer;
-
-import java.nio.charset.StandardCharsets;
-import java.util.Random;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class BufferUtilsTest extends BaseTestSupport {
- public BufferUtilsTest() {
- super();
- }
-
- @Test
- public void testHexEncodeDecode() {
- String expValue = getClass().getName() + "#" + getCurrentTestName();
- byte[] expData = expValue.getBytes(StandardCharsets.UTF_8);
- for (char sep : new char[]{BufferUtils.EMPTY_HEX_SEPARATOR, ':'}) {
- String hexData = BufferUtils.toHex(sep, expData);
- byte[] actData = BufferUtils.decodeHex(sep, hexData);
- String actValue = new String(actData, StandardCharsets.UTF_8);
- String sepName = (BufferUtils.EMPTY_HEX_SEPARATOR == sep) ? "EMPTY" : Character.toString(sep);
- outputDebugMessage("Decode(sep=%s) expected=%s, actual=%s", sepName, expValue, actValue);
- assertArrayEquals("Mismatched result for sep='" + sepName + "'", expData, actData);
- }
- }
-
- @Test
- public void testGetCompactClone() {
- byte[] expected = getCurrentTestName().getBytes(StandardCharsets.UTF_8);
- final int testOffset = Byte.SIZE / 2;
- byte[] data = new byte[expected.length + 2 * testOffset];
- Random rnd = new Random(System.nanoTime());
- rnd.nextBytes(data);
- System.arraycopy(expected, 0, data, testOffset, expected.length);
-
- Buffer buf = ByteArrayBuffer.getCompactClone(data, testOffset, expected.length);
- assertEquals("Mismatched cloned buffer read position", 0, buf.rpos());
- assertEquals("Mismatched cloned buffer available size", expected.length, buf.available());
-
- byte[] actual = buf.array();
- assertNotSame("Original data not cloned", data, actual);
- assertArrayEquals("Mismatched cloned contents", expected, actual);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
deleted file mode 100644
index 8a6cb18..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
+++ /dev/null
@@ -1,163 +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.util.closeable;
-
-import java.io.IOException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.common.Closeable;
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.DefaultCloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class CloseableUtilsTest extends BaseTestSupport {
- public CloseableUtilsTest() {
- super();
- }
-
- @Test
- public void testCloseImmediateNotCalledIfAlreadyClosed() throws IOException {
- Closeable closeable = new IoBaseCloseable() {
- @Override
- public CloseFuture close(boolean immediately) {
- fail("Unexpected call to close(" + immediately + ")");
- return null;
- }
-
- @Override
- public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
- fail("Unexpected call to addCloseFutureListener");
- }
-
- @Override
- public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
- fail("Unexpected call to removeCloseFutureListener");
- }
-
- @Override
- public boolean isClosed() {
- return true;
- }
-
- @Override
- public boolean isClosing() {
- return false;
- }
- };
- closeable.close();
- }
-
- @Test
- public void testCloseImmediateNotCalledIfIsClosing() throws IOException {
- Closeable closeable = new IoBaseCloseable() {
- @Override
- public CloseFuture close(boolean immediately) {
- fail("Unexpected call to close(" + immediately + ")");
- return null;
- }
-
- @Override
- public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
- fail("Unexpected call to addCloseFutureListener");
- }
-
- @Override
- public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
- fail("Unexpected call to removeCloseFutureListener");
- }
-
- @Override
- public boolean isClosed() {
- return false;
- }
-
- @Override
- public boolean isClosing() {
- return true;
- }
- };
- closeable.close();
- }
-
- @Test
- public void testCloseImmediateCalledAndWait() throws Exception {
- DefaultCloseFuture future = new DefaultCloseFuture(this, this);
- AtomicInteger callsCount = new AtomicInteger(0);
- Closeable closeable = new IoBaseCloseable() {
- @Override
- public CloseFuture close(boolean immediately) {
- assertTrue("Closure is not immediate", immediately);
- assertEquals("Multiple close immediate calls", 1, callsCount.incrementAndGet());
- return future;
- }
-
- @Override
- public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
- fail("Unexpected call to addCloseFutureListener");
- }
-
- @Override
- public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
- fail("Unexpected call to removeCloseFutureListener");
- }
-
- @Override
- public boolean isClosed() {
- return false;
- }
-
- @Override
- public boolean isClosing() {
- return false;
- }
- };
-
- ExecutorService service = ThreadUtils.newSingleThreadExecutor(getCurrentTestName());
- try {
- Future<?> task = service.submit((Runnable) () -> {
- try {
- closeable.close();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- });
- future.setClosed(); // signal close complete
- task.get(5L, TimeUnit.SECONDS); // make sure #await call terminated
- assertEquals("Close immediate not called", 1, callsCount.get());
- } finally {
- service.shutdownNow();
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
deleted file mode 100644
index 24f18e3..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
+++ /dev/null
@@ -1,120 +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.util.io;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class EmptyInputStreamTest extends BaseTestSupport {
- public EmptyInputStreamTest() {
- super();
- }
-
- @Test
- public void testEmptyInputStream() throws IOException {
- try (EmptyInputStream in = new EmptyInputStream()) {
- testEmptyInputStream(in, false);
- }
- }
-
- @Test
- public void testCloseableEmptyInputStream() throws IOException {
- try (EmptyInputStream in = new CloseableEmptyInputStream()) {
- testEmptyInputStream(in, true);
- }
- }
-
- private void testEmptyInputStream(InputStream in, boolean failAfterClose) throws IOException {
- testEmptyInputStream("open", in, false);
- in.close();
- testEmptyInputStream("closed", in, failAfterClose);
- }
-
- private void testEmptyInputStream(String message, InputStream in, boolean errorExpected) {
- assertFalse(message + ": unexpected markSupported()", in.markSupported());
- try {
- in.mark(Long.SIZE);
- fail(message + ": unexpected mark success");
- } catch (UnsupportedOperationException e) {
- // expected
- }
-
- try {
- int len = in.available();
- assertFalse(message + ": Unexpected success in available(): " + len, errorExpected);
- assertEquals(message + ": Mismatched available() result", 0, len);
- } catch (IOException e) {
- assertTrue(message + ": Unexpected error on available(): " + e.getMessage(), errorExpected);
- }
-
- try {
- int data = in.read();
- assertFalse(message + ": Unexpected success in read(): " + data, errorExpected);
- assertEquals(message + ": Mismatched read() result", -1, data);
- } catch (IOException e) {
- assertTrue(message + ": Unexpected error on read(): " + e.getMessage(), errorExpected);
- }
-
- byte[] bytes = new byte[Byte.SIZE];
- try {
- int len = in.read(bytes);
- assertFalse(message + ": Unexpected success in read([]): " + BufferUtils.toHex(':', bytes), errorExpected);
- assertEquals(message + ": Mismatched read([]) result", -1, len);
- } catch (IOException e) {
- assertTrue(message + ": Unexpected error on read([]): " + e.getMessage(), errorExpected);
- }
-
- try {
- int len = in.read(bytes, 0, bytes.length);
- assertFalse(message + ": Unexpected success in read([],int,int): " + BufferUtils.toHex(':', bytes), errorExpected);
- assertEquals(message + ": Mismatched read([],int,int) result", -1, len);
- } catch (IOException e) {
- assertTrue(message + ": Unexpected error on read([],int,int): " + e.getMessage(), errorExpected);
- }
-
- try {
- long len = in.skip(Byte.MAX_VALUE);
- assertFalse(message + ": Unexpected success in skip(): " + len, errorExpected);
- assertEquals(message + ": Mismatched skip() result", 0L, len);
- } catch (IOException e) {
- assertTrue(message + ": Unexpected error on skip(): " + e.getMessage(), errorExpected);
- }
-
- try {
- in.reset();
- assertFalse(message + ": Unexpected success in reset()", errorExpected);
- } catch (IOException e) {
- assertTrue(message + ": Unexpected error on reset(): " + e.getMessage(), errorExpected);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/IoUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/IoUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/IoUtilsTest.java
deleted file mode 100644
index 6d7a8fa..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/IoUtilsTest.java
+++ /dev/null
@@ -1,61 +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.util.io;
-
-import java.nio.file.LinkOption;
-
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class IoUtilsTest extends BaseTestSupport {
- public IoUtilsTest() {
- super();
- }
-
- @Test
- public void testFollowLinks() {
- assertTrue("Null ?", IoUtils.followLinks((LinkOption[]) null));
- assertTrue("Empty ?", IoUtils.followLinks(IoUtils.EMPTY_LINK_OPTIONS));
- assertFalse("No-follow ?", IoUtils.followLinks(IoUtils.getLinkOptions(false)));
- }
-
- @Test
- public void testGetEOLBytes() {
- byte[] expected = IoUtils.getEOLBytes();
- assertTrue("Empty bytes", NumberUtils.length(expected) > 0);
-
- for (int index = 1; index < Byte.SIZE; index++) {
- byte[] actual = IoUtils.getEOLBytes();
- assertNotSame("Same bytes received at iteration " + index, expected, actual);
- assertArrayEquals("Mismatched bytes at iteration " + index, expected, actual);
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java
deleted file mode 100644
index 0f179b8..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/LimitInputStreamTest.java
+++ /dev/null
@@ -1,118 +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.util.io;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class LimitInputStreamTest extends BaseTestSupport {
- public LimitInputStreamTest() {
- super();
- }
-
- @Test
- public void testReadLimit() throws IOException {
- Path targetPath = detectTargetFolder();
- Path rootFolder = assertHierarchyTargetFolderExists(targetPath.resolve(getClass().getSimpleName()));
- Path inputFile = rootFolder.resolve(getCurrentTestName() + ".bin");
- byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
- Files.write(inputFile, data);
-
- try (InputStream in = Files.newInputStream(inputFile)) {
- int maxLen = data.length / 2;
- byte[] expected = new byte[maxLen];
- System.arraycopy(data, 0, expected, 0, expected.length);
-
- byte[] actual = new byte[expected.length];
- try (LimitInputStream limited = new LimitInputStream(in, expected.length)) {
- assertTrue("Limited stream not marked as open", limited.isOpen());
- assertEquals("Mismatched initial available data size", expected.length, limited.available());
-
- int readLen = limited.read(actual);
- assertEquals("Incomplete actual data read", actual.length, readLen);
- assertArrayEquals("Mismatched read data", expected, actual);
- assertEquals("Mismatched remaining available data size", 0, limited.available());
-
- readLen = limited.read();
- assertTrue("Unexpected success to read one more byte: " + readLen, readLen < 0);
-
- readLen = limited.read(actual);
- assertTrue("Unexpected success to read extra buffer: " + readLen, readLen < 0);
-
- limited.close();
- assertFalse("Limited stream still marked as open", limited.isOpen());
-
- try {
- readLen = limited.read();
- fail("Unexpected one byte read success after close");
- } catch (IOException e) {
- // expected
- }
-
- try {
- readLen = limited.read(actual);
- fail("Unexpected buffer read success after close: " + readLen);
- } catch (IOException e) {
- // expected
- }
-
- try {
- readLen = limited.read(actual);
- fail("Unexpected buffer read success after close: " + readLen);
- } catch (IOException e) {
- // expected
- }
-
- try {
- readLen = (int) limited.skip(Byte.SIZE);
- fail("Unexpected skip success after close: " + readLen);
- } catch (IOException e) {
- // expected
- }
-
- try {
- readLen = limited.available();
- fail("Unexpected available success after close: " + readLen);
- } catch (IOException e) {
- // expected
- }
- }
-
- // make sure underlying stream not closed
- int readLen = in.read(actual);
- assertEquals("Incomplete extra data read", Math.min(actual.length, data.length - expected.length), readLen);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/ModifiableFileWatcherTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/ModifiableFileWatcherTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/ModifiableFileWatcherTest.java
deleted file mode 100644
index 70c548b..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/ModifiableFileWatcherTest.java
+++ /dev/null
@@ -1,82 +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.util.io;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.attribute.PosixFilePermission;
-import java.util.Collection;
-import java.util.Date;
-import java.util.Map;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.OsUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class ModifiableFileWatcherTest extends BaseTestSupport {
- public ModifiableFileWatcherTest() {
- super();
- }
-
- @Test // see SSHD-606
- public void testValidateStrictConfigFilePermissions() throws IOException {
- Path file = getTempTargetRelativeFile(getClass().getSimpleName(), getCurrentTestName());
- outputDebugMessage("%s deletion result=%s", file, Files.deleteIfExists(file));
- assertNull("Unexpected violation for non-existent file: " + file, ModifiableFileWatcher.validateStrictConfigFilePermissions(file));
-
- assertHierarchyTargetFolderExists(file.getParent());
- try (OutputStream output = Files.newOutputStream(file)) {
- output.write((getClass().getName() + "#" + getCurrentTestName() + "@" + new Date(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
- }
-
- Collection<PosixFilePermission> perms = IoUtils.getPermissions(file);
- if (GenericUtils.isEmpty(perms)) {
- assertNull("Unexpected violation for no permissions file: " + file, ModifiableFileWatcher.validateStrictConfigFilePermissions(file));
- } else if (OsUtils.isUNIX()) {
- Map.Entry<String, Object> violation = null;
- for (PosixFilePermission p : ModifiableFileWatcher.STRICTLY_PROHIBITED_FILE_PERMISSION) {
- if (perms.contains(p)) {
- violation = ModifiableFileWatcher.validateStrictConfigFilePermissions(file);
- assertNotNull("Unexpected success for permission=" + p + " of file " + file + " permissions=" + perms, violation);
- break;
- }
- }
-
- if (violation == null) { // we do not expected a failure if no permissions have been violated
- assertNull("Unexpected UNIX violation for file " + file + " permissions=" + perms, ModifiableFileWatcher.validateStrictConfigFilePermissions(file));
- }
- } else {
- assertNull("Unexpected Windows violation for file " + file + " permissions=" + perms, ModifiableFileWatcher.validateStrictConfigFilePermissions(file));
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java
deleted file mode 100644
index 2990fa6..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseInputStreamTest.java
+++ /dev/null
@@ -1,89 +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.util.io;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Date;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class NoCloseInputStreamTest extends BaseTestSupport {
- public NoCloseInputStreamTest() {
- super();
- }
-
- @Test
- public void testCanKeepReadingAfterClose() throws IOException {
- byte[] expected = (getClass().getName() + "#" + getCurrentTestName() + "@" + new Date()).getBytes(StandardCharsets.UTF_8);
- Path dir = createTempClassFolder();
- Path file = Files.write(dir.resolve(getCurrentTestName() + ".txt"), expected);
- try (InputStream fileStream = Files.newInputStream(file);
- InputStream shielded = new NoCloseInputStream(fileStream)) {
- int index = 0;
-
- for (; index < (expected.length / 2); index++) {
- shielded.close();
-
- int readValue = shielded.read();
- if (readValue == -1) {
- fail("Premature EOF after shield read of " + index + " bytes");
- }
-
- byte expValue = expected[index];
- byte actValue = (byte) (readValue & 0xFF);
- if (expValue != actValue) {
- fail("Mismatched shielded read value after " + index + " bytes");
- }
- }
-
- for (; index < expected.length; index++) {
- int readValue = fileStream.read();
- if (readValue == -1) {
- fail("Premature EOF after original read of " + index + " bytes");
- }
- byte expValue = expected[index];
- byte actValue = (byte) (readValue & 0xFF);
- if (expValue != actValue) {
- fail("Mismatched original read value after " + index + " bytes");
- }
- }
-
- int readValue = shielded.read();
- assertEquals("Shielded EOF not signalled", -1, readValue);
-
- readValue = fileStream.read();
- assertEquals("Original EOF not signalled", -1, readValue);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java
deleted file mode 100644
index fc211c3..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/io/NoCloseOutputStreamTest.java
+++ /dev/null
@@ -1,69 +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.util.io;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Date;
-
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runners.MethodSorters;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Category({ NoIoTestCase.class })
-public class NoCloseOutputStreamTest extends BaseTestSupport {
- public NoCloseOutputStreamTest() {
- super();
- }
-
- @Test
- public void testCanKeepWritingAfterClose() throws IOException {
- Path dir = createTempClassFolder();
- Path file = dir.resolve(getCurrentTestName() + ".txt");
- Files.deleteIfExists(file);
-
- String expectedOutput = getClass().getName() + "#" + getCurrentTestName() + "@" + new Date();
- byte[] expected = expectedOutput.getBytes(StandardCharsets.UTF_8);
- try (OutputStream fileStream = Files.newOutputStream(file);
- OutputStream shielded = new NoCloseOutputStream(fileStream)) {
- int index = 0;
- for (; index < (expected.length / 2); index++) {
- shielded.close();
- shielded.write(expected[index] & 0xFF);
- }
-
- fileStream.write(expected, index, expected.length - index);
- }
-
- byte[] actual = Files.readAllBytes(file);
- String actualOutput = new String(actual, StandardCharsets.UTF_8);
- assertEquals(expectedOutput, actualOutput);
- }
-}
[38/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
new file mode 100644
index 0000000..c13cd9e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
@@ -0,0 +1,604 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.buffer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.util.function.IntUnaryOperator;
+import java.util.logging.Level;
+
+import org.apache.sshd.common.PropertyResolver;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.logging.SimplifiedLog;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class BufferUtils {
+ public static final char DEFAULT_HEX_SEPARATOR = ' ';
+ public static final char EMPTY_HEX_SEPARATOR = '\0';
+ public static final String HEX_DIGITS = "0123456789abcdef";
+
+ public static final String HEXDUMP_CHUNK_SIZE = "sshd-hexdump-chunk-size";
+ public static final int DEFAULT_HEXDUMP_CHUNK_SIZE = 64;
+ public static final Level DEFAULT_HEXDUMP_LEVEL = Level.FINEST;
+
+ public static final IntUnaryOperator DEFAULT_BUFFER_GROWTH_FACTOR = BufferUtils::getNextPowerOf2;
+
+ /**
+ * Maximum value of a {@code uint32} field
+ */
+ public static final long MAX_UINT32_VALUE = 0x0FFFFFFFFL;
+
+ /**
+ * Maximum value of a {@code uint8} field
+ */
+ public static final int MAX_UINT8_VALUE = 0x0FF;
+
+ /**
+ * Private Constructor
+ */
+ private BufferUtils() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+
+ public static void dumpHex(SimplifiedLog logger, Level level, String prefix, PropertyResolver resolver, char sep, byte... data) {
+ dumpHex(logger, level, prefix, resolver, sep, data, 0, NumberUtils.length(data));
+ }
+
+ public static void dumpHex(SimplifiedLog logger, Level level, String prefix, PropertyResolver resolver, char sep, byte[] data, int offset, int len) {
+ dumpHex(logger, level, prefix, sep, resolver.getIntProperty(HEXDUMP_CHUNK_SIZE, DEFAULT_HEXDUMP_CHUNK_SIZE), data, offset, len);
+ }
+
+ public static void dumpHex(SimplifiedLog logger, Level level, String prefix, char sep, int chunkSize, byte... data) {
+ dumpHex(logger, level, prefix, sep, chunkSize, data, 0, NumberUtils.length(data));
+ }
+
+ public static void dumpHex(SimplifiedLog logger, Level level, String prefix, char sep, int chunkSize, byte[] data, int offset, int len) {
+ if ((logger == null) || (level == null) || (!logger.isEnabled(level))) {
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder(chunkSize * 3 /* HEX */ + prefix.length() + Long.SIZE /* some extra */);
+ sb.append(prefix);
+ for (int remainLen = len, chunkIndex = 1, curOffset = offset, totalLen = 0; remainLen > 0; chunkIndex++) {
+ sb.setLength(prefix.length()); // reset for next chunk
+
+ sb.append(" [chunk #").append(chunkIndex).append(']');
+
+ int dumpSize = Math.min(chunkSize, remainLen);
+ totalLen += dumpSize;
+ sb.append('(').append(totalLen).append('/').append(len).append(')');
+
+ try {
+ appendHex(sb.append(' '), data, curOffset, dumpSize, sep);
+ } catch (IOException e) { // unexpected
+ sb.append(e.getClass().getSimpleName()).append(": ").append(e.getMessage());
+ }
+
+ // Pad the last (incomplete) line to align its data view
+ for (int index = dumpSize; index < chunkSize; index++) {
+ if (sep != EMPTY_HEX_SEPARATOR) {
+ sb.append(' ');
+ }
+ sb.append(" ");
+ }
+
+ sb.append(" ");
+ for (int pos = curOffset, l = 0; l < dumpSize; pos++, l++) {
+ int b = data[pos] & 0xFF;
+ if ((b > ' ') && (b < 0x7E)) {
+ sb.append((char) b);
+ } else {
+ sb.append('.');
+ }
+ }
+
+ logger.log(level, sb.toString());
+ remainLen -= dumpSize;
+ curOffset += dumpSize;
+ }
+ }
+
+ public static String toHex(byte... array) {
+ return toHex(array, 0, NumberUtils.length(array));
+ }
+
+ public static String toHex(char sep, byte... array) {
+ return toHex(array, 0, NumberUtils.length(array), sep);
+ }
+
+ public static String toHex(byte[] array, int offset, int len) {
+ return toHex(array, offset, len, DEFAULT_HEX_SEPARATOR);
+ }
+
+ public static String toHex(byte[] array, int offset, int len, char sep) {
+ if (len <= 0) {
+ return "";
+ }
+
+ try {
+ return appendHex(new StringBuilder(len * 3 /* 2 HEX + sep */), array, offset, len, sep).toString();
+ } catch (IOException e) { // unexpected
+ return e.getClass().getSimpleName() + ": " + e.getMessage();
+ }
+ }
+
+ public static <A extends Appendable> A appendHex(A sb, char sep, byte... array) throws IOException {
+ return appendHex(sb, array, 0, NumberUtils.length(array), sep);
+ }
+
+ public static <A extends Appendable> A appendHex(A sb, byte[] array, int offset, int len, char sep) throws IOException {
+ if (len <= 0) {
+ return sb;
+ }
+
+ for (int curOffset = offset, maxOffset = offset + len; curOffset < maxOffset; curOffset++) {
+ byte b = array[curOffset];
+ if ((curOffset > offset) && (sep != EMPTY_HEX_SEPARATOR)) {
+ sb.append(sep);
+ }
+ sb.append(HEX_DIGITS.charAt((b >> 4) & 0x0F));
+ sb.append(HEX_DIGITS.charAt(b & 0x0F));
+ }
+
+ return sb;
+ }
+
+ /**
+ * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
+ * @param csq The {@link CharSequence} containing the HEX encoded bytes
+ * @return The decoded bytes
+ * @throws IllegalArgumentException If invalid HEX sequence length
+ * @throws NumberFormatException If invalid HEX characters found
+ * @see #decodeHex(char, CharSequence, int, int)
+ */
+ public static byte[] decodeHex(char separator, CharSequence csq) {
+ return decodeHex(separator, csq, 0, GenericUtils.length(csq));
+ }
+
+ /**
+ * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
+ * @param csq The {@link CharSequence} containing the HEX encoded bytes
+ * @param start Start offset of the HEX sequence (inclusive)
+ * @param end End offset of the HEX sequence (exclusive)
+ * @return The decoded bytes
+ * @throws IllegalArgumentException If invalid HEX sequence length
+ * @throws NumberFormatException If invalid HEX characters found
+ */
+ public static byte[] decodeHex(char separator, CharSequence csq, int start, int end) {
+ int len = end - start;
+ ValidateUtils.checkTrue(len >= 0, "Bad HEX sequence length: %d", len);
+ if (len == 0) {
+ return GenericUtils.EMPTY_BYTE_ARRAY;
+ }
+
+ int delta = 2;
+ byte[] bytes;
+ if (separator != EMPTY_HEX_SEPARATOR) {
+ // last character cannot be the separator
+ ValidateUtils.checkTrue((len % 3) == 2, "Invalid separated HEX sequence length: %d", len);
+ bytes = new byte[(len + 1) / 3];
+ delta++;
+ } else {
+ ValidateUtils.checkTrue((len & 0x01) == 0, "Invalid contiguous HEX sequence length: %d", len);
+ bytes = new byte[len >>> 1];
+ }
+
+ int writeLen = 0;
+ for (int curPos = start; curPos < end; curPos += delta, writeLen++) {
+ bytes[writeLen] = fromHex(csq.charAt(curPos), csq.charAt(curPos + 1));
+ }
+ assert writeLen == bytes.length;
+
+ return bytes;
+ }
+
+ /**
+ * @param <S> The {@link OutputStream} generic type
+ * @param stream The target {@link OutputStream}
+ * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
+ * @param csq The {@link CharSequence} containing the HEX encoded bytes
+ * @return The number of bytes written to the stream
+ * @throws IOException If failed to write
+ * @throws IllegalArgumentException If invalid HEX sequence length
+ * @throws NumberFormatException If invalid HEX characters found
+ * @see #decodeHex(OutputStream, char, CharSequence, int, int)
+ */
+ public static <S extends OutputStream> int decodeHex(S stream, char separator, CharSequence csq) throws IOException {
+ return decodeHex(stream, separator, csq, 0, GenericUtils.length(csq));
+ }
+
+ /**
+ * @param <S> The {@link OutputStream} generic type
+ * @param stream The target {@link OutputStream}
+ * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
+ * @param csq The {@link CharSequence} containing the HEX encoded bytes
+ * @param start Start offset of the HEX sequence (inclusive)
+ * @param end End offset of the HEX sequence (exclusive)
+ * @return The number of bytes written to the stream
+ * @throws IOException If failed to write
+ * @throws IllegalArgumentException If invalid HEX sequence length
+ * @throws NumberFormatException If invalid HEX characters found
+ */
+ public static <S extends OutputStream> int decodeHex(S stream, char separator, CharSequence csq, int start, int end) throws IOException {
+ int len = end - start;
+ ValidateUtils.checkTrue(len >= 0, "Bad HEX sequence length: %d", len);
+
+ int delta = 2;
+ if (separator != EMPTY_HEX_SEPARATOR) {
+ // last character cannot be the separator
+ ValidateUtils.checkTrue((len % 3) == 2, "Invalid separated HEX sequence length: %d", len);
+ delta++;
+ } else {
+ ValidateUtils.checkTrue((len & 0x01) == 0, "Invalid contiguous HEX sequence length: %d", len);
+ }
+
+ int writeLen = 0;
+ for (int curPos = start; curPos < end; curPos += delta, writeLen++) {
+ stream.write(fromHex(csq.charAt(curPos), csq.charAt(curPos + 1)) & 0xFF);
+ }
+
+ return writeLen;
+ }
+
+ public static byte fromHex(char hi, char lo) throws NumberFormatException {
+ int hiValue = HEX_DIGITS.indexOf(((hi >= 'A') && (hi <= 'F')) ? ('a' + (hi - 'A')) : hi);
+ int loValue = HEX_DIGITS.indexOf(((lo >= 'A') && (lo <= 'F')) ? ('a' + (lo - 'A')) : lo);
+ if ((hiValue < 0) || (loValue < 0)) {
+ throw new NumberFormatException("fromHex(" + new String(new char[]{hi, lo}) + ") non-HEX characters");
+ }
+
+ return (byte) ((hiValue << 4) + loValue);
+ }
+
+ /**
+ * Read a 32-bit value in network order
+ *
+ * @param input The {@link InputStream}
+ * @param buf Work buffer to use
+ * @return The read 32-bit value
+ * @throws IOException If failed to read 4 bytes or not enough room in
+ * @see #readInt(InputStream, byte[], int, int)
+ */
+ public static int readInt(InputStream input, byte[] buf) throws IOException {
+ return readInt(input, buf, 0, NumberUtils.length(buf));
+ }
+
+ /**
+ * Read a 32-bit value in network order
+ *
+ * @param input The {@link InputStream}
+ * @param buf Work buffer to use
+ * @param offset Offset in buffer to us
+ * @param len Available length - must have at least 4 bytes available
+ * @return The read 32-bit value
+ * @throws IOException If failed to read 4 bytes or not enough room in
+ * work buffer
+ * @see #readUInt(InputStream, byte[], int, int)
+ */
+ public static int readInt(InputStream input, byte[] buf, int offset, int len) throws IOException {
+ return (int) readUInt(input, buf, offset, len);
+ }
+
+ /**
+ * Read a 32-bit value in network order
+ *
+ * @param input The {@link InputStream}
+ * @param buf Work buffer to use
+ * @return The read 32-bit value
+ * @throws IOException If failed to read 4 bytes or not enough room in
+ * @see #readUInt(InputStream, byte[], int, int)
+ */
+ public static long readUInt(InputStream input, byte[] buf) throws IOException {
+ return readUInt(input, buf, 0, NumberUtils.length(buf));
+ }
+
+ /**
+ * Read a 32-bit value in network order
+ *
+ * @param input The {@link InputStream}
+ * @param buf Work buffer to use
+ * @param offset Offset in buffer to us
+ * @param len Available length - must have at least 4 bytes available
+ * @return The read 32-bit value
+ * @throws IOException If failed to read 4 bytes or not enough room in
+ * work buffer
+ * @see #getUInt(byte[], int, int)
+ */
+ public static long readUInt(InputStream input, byte[] buf, int offset, int len) throws IOException {
+ try {
+ if (len < Integer.BYTES) {
+ throw new IllegalArgumentException("Not enough data for a UINT: required=" + Integer.BYTES + ", available=" + len);
+ }
+
+ IoUtils.readFully(input, buf, offset, Integer.BYTES);
+ return getUInt(buf, offset, len);
+ } catch (RuntimeException | Error e) {
+ throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
+ + " to read UINT value: " + e.getMessage());
+ }
+ }
+
+ /**
+ * @param buf A buffer holding a 32-bit unsigned integer in <B>big endian</B>
+ * format. <B>Note:</B> if more than 4 bytes are available, then only the
+ * <U>first</U> 4 bytes in the buffer will be used
+ * @return The result as a {@code long} whose 32 high-order bits are zero
+ * @see #getUInt(byte[], int, int)
+ */
+ public static long getUInt(byte... buf) {
+ return getUInt(buf, 0, NumberUtils.length(buf));
+ }
+
+ /**
+ * @param buf A buffer holding a 32-bit unsigned integer in <B>big endian</B>
+ * format.
+ * @param off The offset of the data in the buffer
+ * @param len The available data length. <B>Note:</B> if more than 4 bytes
+ * are available, then only the <U>first</U> 4 bytes in the buffer will be
+ * used (starting at the specified <tt>offset</tt>)
+ * @return The result as a {@code long} whose 32 high-order bits are zero
+ */
+ public static long getUInt(byte[] buf, int off, int len) {
+ if (len < Integer.BYTES) {
+ throw new IllegalArgumentException("Not enough data for a UINT: required=" + Integer.BYTES + ", available=" + len);
+ }
+
+ long l = (buf[off] << 24) & 0xff000000L;
+ l |= (buf[off + 1] << 16) & 0x00ff0000L;
+ l |= (buf[off + 2] << 8) & 0x0000ff00L;
+ l |= (buf[off + 3]) & 0x000000ffL;
+ return l;
+ }
+
+ /**
+ * Writes a 32-bit value in network order (i.e., MSB 1st)
+ *
+ * @param output The {@link OutputStream} to write the value
+ * @param value The 32-bit value
+ * @param buf A work buffer to use - must have enough space to contain 4 bytes
+ * @throws IOException If failed to write the value or work buffer to small
+ * @see #writeInt(OutputStream, int, byte[], int, int)
+ */
+ public static void writeInt(OutputStream output, int value, byte[] buf) throws IOException {
+ writeUInt(output, value, buf, 0, NumberUtils.length(buf));
+ }
+
+ /**
+ * Writes a 32-bit value in network order (i.e., MSB 1st)
+ *
+ * @param output The {@link OutputStream} to write the value
+ * @param value The 32-bit value
+ * @param buf A work buffer to use - must have enough space to contain 4 bytes
+ * @param off The offset to write the value
+ * @param len The available space
+ * @throws IOException If failed to write the value or work buffer to small
+ * @see #writeUInt(OutputStream, long, byte[], int, int)
+ */
+ public static void writeInt(OutputStream output, int value, byte[] buf, int off, int len) throws IOException {
+ writeUInt(output, value & 0xFFFFFFFFL, buf, off, len);
+ }
+
+ /**
+ * Writes a 32-bit value in network order (i.e., MSB 1st)
+ *
+ * @param output The {@link OutputStream} to write the value
+ * @param value The 32-bit value
+ * @param buf A work buffer to use - must have enough space to contain 4 bytes
+ * @throws IOException If failed to write the value or work buffer to small
+ * @see #writeUInt(OutputStream, long, byte[], int, int)
+ */
+ public static void writeUInt(OutputStream output, long value, byte[] buf) throws IOException {
+ writeUInt(output, value, buf, 0, NumberUtils.length(buf));
+ }
+
+ /**
+ * Writes a 32-bit value in network order (i.e., MSB 1st)
+ *
+ * @param output The {@link OutputStream} to write the value
+ * @param value The 32-bit value
+ * @param buf A work buffer to use - must have enough space to contain 4 bytes
+ * @param off The offset to write the value
+ * @param len The available space
+ * @throws IOException If failed to write the value or work buffer to small
+ * @see #putUInt(long, byte[], int, int)
+ */
+ public static void writeUInt(OutputStream output, long value, byte[] buf, int off, int len) throws IOException {
+ try {
+ int writeLen = putUInt(value, buf, off, len);
+ output.write(buf, off, writeLen);
+ } catch (RuntimeException | Error e) {
+ throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
+ + " to write UINT value=" + value + ": " + e.getMessage());
+ }
+ }
+
+ /**
+ * Writes a 32-bit value in network order (i.e., MSB 1st)
+ *
+ * @param value The 32-bit value
+ * @param buf The buffer
+ * @return The number of bytes used in the buffer
+ * @throws IllegalArgumentException if not enough space available
+ * @see #putUInt(long, byte[], int, int)
+ */
+ public static int putUInt(long value, byte[] buf) {
+ return putUInt(value, buf, 0, NumberUtils.length(buf));
+ }
+
+ /**
+ * Writes a 32-bit value in network order (i.e., MSB 1st)
+ *
+ * @param value The 32-bit value
+ * @param buf The buffer
+ * @param off The offset to write the value
+ * @param len The available space
+ * @return The number of bytes used in the buffer
+ * @throws IllegalArgumentException if not enough space available
+ */
+ public static int putUInt(long value, byte[] buf, int off, int len) {
+ if (len < Integer.BYTES) {
+ throw new IllegalArgumentException("Not enough data for a UINT: required=" + Integer.BYTES + ", available=" + len);
+ }
+
+ buf[off] = (byte) ((value >> 24) & 0xFF);
+ buf[off + 1] = (byte) ((value >> 16) & 0xFF);
+ buf[off + 2] = (byte) ((value >> 8) & 0xFF);
+ buf[off + 3] = (byte) (value & 0xFF);
+
+ return Integer.BYTES;
+ }
+
+ public static boolean equals(byte[] a1, byte[] a2) {
+ int len1 = NumberUtils.length(a1);
+ int len2 = NumberUtils.length(a2);
+ if (len1 != len2) {
+ return false;
+ } else {
+ return equals(a1, 0, a2, 0, len1);
+ }
+ }
+
+ @SuppressWarnings("PMD.AssignmentInOperand")
+ public static boolean equals(byte[] a1, int a1Offset, byte[] a2, int a2Offset, int length) {
+ int len1 = NumberUtils.length(a1);
+ int len2 = NumberUtils.length(a2);
+ if ((len1 < (a1Offset + length)) || (len2 < (a2Offset + length))) {
+ return false;
+ }
+
+ while (length-- > 0) {
+ if (a1[a1Offset++] != a2[a2Offset++]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static int getNextPowerOf2(int value) {
+ // for 0-7 return 8
+ return (value < Byte.SIZE) ? Byte.SIZE : NumberUtils.getNextPowerOf2(value);
+ }
+
+ /**
+ * Used for encodings where we don't know the data length before adding it
+ * to the buffer. The idea is to place a 32-bit "placeholder",
+ * encode the data and then return back to the placeholder and update the
+ * length. The method calculates the encoded data length, moves the write
+ * position to the specified placeholder position, updates the length value
+ * and then moves the write position it back to its original value.
+ *
+ * @param buffer The {@link Buffer}
+ * @param lenPos The offset in the buffer where the length placeholder is
+ * to be update - <B>Note:</B> assumption is that the encoded data starts
+ * <U>immediately</U> after the placeholder
+ * @return The amount of data that has been encoded
+ */
+ public static int updateLengthPlaceholder(Buffer buffer, int lenPos) {
+ int startPos = lenPos + Integer.BYTES;
+ int endPos = buffer.wpos();
+ int dataLength = endPos - startPos;
+ // NOTE: although data length is defined as UINT32, we do not expected sizes above Integer.MAX_VALUE
+ ValidateUtils.checkTrue(dataLength >= 0, "Illegal data length: %d", dataLength);
+ buffer.wpos(lenPos);
+ buffer.putInt(dataLength);
+ buffer.wpos(endPos);
+ return dataLength;
+ }
+
+ /**
+ * Updates a 32-bit "placeholder" location for data length - moves
+ * the write position to the specified placeholder position, updates the length
+ * value and then moves the write position it back to its original value.
+ *
+ * @param buffer The {@link Buffer}
+ * @param lenPos The offset in the buffer where the length placeholder is
+ * to be update - <B>Note:</B> assumption is that the encoded data starts
+ * <U>immediately</U> after the placeholder
+ * @param dataLength The length to update
+ */
+ public static void updateLengthPlaceholder(Buffer buffer, int lenPos, int dataLength) {
+ int curPos = buffer.wpos();
+ buffer.wpos(lenPos);
+ buffer.putInt(dataLength);
+ buffer.wpos(curPos);
+ }
+
+ /**
+ * Invokes {@link Buffer#clear()}
+ *
+ * @param <B> The generic buffer type
+ * @param buffer A {@link Buffer} instance - ignored if {@code null}
+ * @return The same as the input instance
+ */
+ public static <B extends Buffer> B clear(B buffer) {
+ if (buffer != null) {
+ buffer.clear();
+ }
+
+ return buffer;
+ }
+
+ public static long validateInt32Value(long value, String message) {
+ ValidateUtils.checkTrue(isValidInt32Value(value), message, value);
+ return value;
+ }
+
+ public static long validateInt32Value(long value, String format, Object arg) {
+ ValidateUtils.checkTrue(isValidInt32Value(value), format, arg);
+ return value;
+ }
+
+ public static long validateInt32Value(long value, String format, Object... args) {
+ ValidateUtils.checkTrue(isValidInt32Value(value), format, args);
+ return value;
+ }
+
+ public static boolean isValidInt32Value(long value) {
+ return (value >= Integer.MIN_VALUE) && (value <= Integer.MAX_VALUE);
+ }
+
+ public static long validateUint32Value(long value, String message) {
+ ValidateUtils.checkTrue(isValidUint32Value(value), message, value);
+ return value;
+ }
+
+ public static long validateUint32Value(long value, String format, Object arg) {
+ ValidateUtils.checkTrue(isValidUint32Value(value), format, arg);
+ return value;
+ }
+
+ public static long validateUint32Value(long value, String format, Object... args) {
+ ValidateUtils.checkTrue(isValidUint32Value(value), format, args);
+ return value;
+ }
+
+ public static boolean isValidUint32Value(long value) {
+ return (value >= 0L) && (value <= MAX_UINT32_VALUE);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
new file mode 100644
index 0000000..6655ec1
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
@@ -0,0 +1,253 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.buffer;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.function.IntUnaryOperator;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.Readable;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Provides an implementation of {@link Buffer} using a backing byte array
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ByteArrayBuffer extends Buffer {
+ public static final int DEFAULT_SIZE = 256;
+ public static final int MAX_LEN = 65536;
+
+ private byte[] data;
+ private int rpos;
+ private int wpos;
+
+ public ByteArrayBuffer() {
+ this(DEFAULT_SIZE);
+ }
+
+ public ByteArrayBuffer(int size) {
+ this(size, true);
+ }
+
+ public ByteArrayBuffer(int size, boolean roundOff) {
+ this(new byte[roundOff ? BufferUtils.getNextPowerOf2(size) : size], false);
+ }
+
+ public ByteArrayBuffer(byte[] data) {
+ this(data, 0, data.length, true);
+ }
+
+ public ByteArrayBuffer(byte[] data, boolean read) {
+ this(data, 0, data.length, read);
+ }
+
+ public ByteArrayBuffer(byte[] data, int off, int len) {
+ this(data, off, len, true);
+ }
+
+ public ByteArrayBuffer(byte[] data, int off, int len, boolean read) {
+ this.data = data;
+ this.rpos = off;
+ this.wpos = (read ? len : 0) + off;
+ }
+
+ @Override
+ public int rpos() {
+ return rpos;
+ }
+
+ @Override
+ public void rpos(int rpos) {
+ this.rpos = rpos;
+ }
+
+ @Override
+ public int wpos() {
+ return wpos;
+ }
+
+ @Override
+ public void wpos(int wpos) {
+ if (wpos > this.wpos) {
+ ensureCapacity(wpos - this.wpos);
+ }
+ this.wpos = wpos;
+ }
+
+ @Override
+ public int available() {
+ return wpos - rpos;
+ }
+
+ @Override
+ public int capacity() {
+ return data.length - wpos;
+ }
+
+ @Override
+ public byte[] array() {
+ return data;
+ }
+
+ @Override
+ public void compact() {
+ int avail = available();
+ if (avail > 0) {
+ System.arraycopy(data, rpos, data, 0, avail);
+ }
+ wpos -= rpos;
+ rpos = 0;
+ }
+
+ @Override
+ public void clear(boolean wipeData) {
+ rpos = 0;
+ wpos = 0;
+
+ if (wipeData) {
+ Arrays.fill(data, (byte) 0);
+ }
+ }
+
+ @Override
+ public byte getByte() {
+ ensureAvailable(Byte.BYTES);
+ return data[rpos++];
+ }
+
+ @Override
+ public void putByte(byte b) {
+ ensureCapacity(Byte.BYTES);
+ data[wpos++] = b;
+ }
+
+ @Override
+ public int putBuffer(Readable buffer, boolean expand) {
+ int r = expand ? buffer.available() : Math.min(buffer.available(), capacity());
+ ensureCapacity(r);
+ buffer.getRawBytes(data, wpos, r);
+ wpos += r;
+ return r;
+ }
+
+ @Override
+ public void putBuffer(ByteBuffer buffer) {
+ int required = buffer.remaining();
+ ensureCapacity(required + Integer.SIZE);
+ putInt(required);
+ buffer.get(data, wpos, required);
+ wpos += required;
+ }
+
+ @Override
+ public void putRawBytes(byte[] d, int off, int len) {
+ ValidateUtils.checkTrue(len >= 0, "Negative raw bytes length: %d", len);
+ ensureCapacity(len);
+ System.arraycopy(d, off, data, wpos, len);
+ wpos += len;
+ }
+
+ @Override
+ public String getString(Charset charset) {
+ int len = getInt();
+ if (len < 0) {
+ throw new BufferException("Bad item length: " + len);
+ }
+ ensureAvailable(len);
+
+ Objects.requireNonNull(charset, "No charset specified");
+ String s = new String(data, rpos, len, charset);
+ rpos += len;
+ return s;
+ }
+
+ @Override
+ public void getRawBytes(byte[] buf, int off, int len) {
+ ensureAvailable(len);
+ copyRawBytes(0, buf, off, len);
+ rpos += len;
+ }
+
+ @Override
+ protected void copyRawBytes(int offset, byte[] buf, int pos, int len) {
+ System.arraycopy(data, rpos + offset, buf, pos, len);
+ }
+
+ @Override
+ public void ensureCapacity(int capacity, IntUnaryOperator growthFactor) {
+ ValidateUtils.checkTrue(capacity >= 0, "Negative capacity requested: %d", capacity);
+
+ int maxSize = size();
+ int curPos = wpos();
+ int remaining = maxSize - curPos;
+ if (remaining < capacity) {
+ int minimum = curPos + capacity;
+ int actual = growthFactor.applyAsInt(minimum);
+ if (actual < minimum) {
+ throw new IllegalStateException("ensureCapacity(" + capacity + ") actual (" + actual + ") below min. (" + minimum + ")");
+ }
+ byte[] tmp = new byte[actual];
+ System.arraycopy(data, 0, tmp, 0, data.length);
+ data = tmp;
+ }
+ }
+
+ @Override
+ protected int size() {
+ return data.length;
+ }
+
+ /**
+ * Creates a compact buffer (i.e., one that starts at offset zero) containing a <U>copy</U>
+ * of the original data
+ *
+ * @param data The original data buffer
+ * @return A {@link ByteArrayBuffer} containing a <U>copy</U> of the original data
+ * starting at zero read position
+ * @see #getCompactClone(byte[], int, int)
+ */
+ public static ByteArrayBuffer getCompactClone(byte[] data) {
+ return getCompactClone(data, 0, NumberUtils.length(data));
+ }
+
+ /**
+ * Creates a compact buffer (i.e., one that starts at offset zero) containing a <U>copy</U>
+ * of the original data
+ *
+ * @param data The original data buffer
+ * @param offset The offset of the valid data in the buffer
+ * @param len The size (in bytes) of of the valid data in the buffer
+ * @return A {@link ByteArrayBuffer} containing a <U>copy</U> of the original data
+ * starting at zero read position
+ */
+ public static ByteArrayBuffer getCompactClone(byte[] data, int offset, int len) {
+ byte[] clone = (len > 0) ? new byte[len] : GenericUtils.EMPTY_BYTE_ARRAY;
+ if (len > 0) {
+ System.arraycopy(data, offset, clone, 0, len);
+ }
+
+ return new ByteArrayBuffer(clone, true);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/AbstractBufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/AbstractBufferPublicKeyParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/AbstractBufferPublicKeyParser.java
new file mode 100644
index 0000000..aaf9641
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/AbstractBufferPublicKeyParser.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.buffer.keys;
+
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @param <PUB> Type of {@link PublicKey} being extracted
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractBufferPublicKeyParser<PUB extends PublicKey> implements BufferPublicKeyParser<PUB> {
+ private final Class<PUB> keyClass;
+ private final Collection<String> supported;
+
+ protected AbstractBufferPublicKeyParser(Class<PUB> keyClass, String... supported) {
+ this(keyClass, GenericUtils.isEmpty(supported) ? Collections.emptyList() : Arrays.asList(supported));
+ }
+
+ protected AbstractBufferPublicKeyParser(Class<PUB> keyClass, Collection<String> supported) {
+ this.keyClass = Objects.requireNonNull(keyClass, "No key class");
+ this.supported = ValidateUtils.checkNotNullAndNotEmpty(supported, "No supported types for %s", keyClass.getSimpleName());
+ }
+
+ public Collection<String> getSupportedKeyTypes() {
+ return supported;
+ }
+
+ public final Class<PUB> getKeyClass() {
+ return keyClass;
+ }
+
+ @Override
+ public boolean isKeyTypeSupported(String keyType) {
+ Collection<String> keys = getSupportedKeyTypes();
+ return (GenericUtils.length(keyType) > 0)
+ && (GenericUtils.size(keys) > 0)
+ && keys.contains(keyType);
+ }
+
+ protected <S extends KeySpec> PUB generatePublicKey(String algorithm, S keySpec) throws GeneralSecurityException {
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ PublicKey key = keyFactory.generatePublic(keySpec);
+ Class<PUB> kc = getKeyClass();
+ if (!kc.isInstance(key)) {
+ throw new InvalidKeySpecException("Mismatched generated key types: expected=" + kc.getSimpleName() + ", actual=" + key);
+ }
+
+ return kc.cast(key);
+ }
+
+ protected KeyFactory getKeyFactory(String algorithm) throws GeneralSecurityException {
+ return SecurityUtils.getKeyFactory(algorithm);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " - supported=" + getSupportedKeyTypes();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java
new file mode 100644
index 0000000..6f7cde6
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.buffer.keys;
+
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * Parses a raw {@link PublicKey} from a {@link Buffer}
+ *
+ * @param <PUB> Type of {@link PublicKey} being extracted
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface BufferPublicKeyParser<PUB extends PublicKey> {
+
+ BufferPublicKeyParser<PublicKey> EMPTY = new BufferPublicKeyParser<PublicKey>() {
+ @Override
+ public boolean isKeyTypeSupported(String keyType) {
+ return false;
+ }
+
+ @Override
+ public PublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
+ throw new NoSuchAlgorithmException(keyType);
+ }
+
+ @Override
+ public String toString() {
+ return "EMPTY";
+ }
+ };
+
+ BufferPublicKeyParser<PublicKey> DEFAULT = aggregate(
+ Arrays.asList(
+ RSABufferPublicKeyParser.INSTANCE,
+ DSSBufferPublicKeyParser.INSTANCE,
+ ECBufferPublicKeyParser.INSTANCE,
+ ED25519BufferPublicKeyParser.INSTANCE));
+
+ /**
+ * @param keyType The key type - e.g., "ssh-rsa", "ssh-dss"
+ * @return {@code true} if this key type is supported by the parser
+ */
+ boolean isKeyTypeSupported(String keyType);
+
+ /**
+ * @param keyType The key type - e.g., "ssh-rsa", "ssh-dss"
+ * @param buffer The {@link Buffer} containing the encoded raw public key
+ * @return The decoded {@link PublicKey}
+ * @throws GeneralSecurityException If failed to generate the key
+ */
+ PUB getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException;
+
+ static BufferPublicKeyParser<PublicKey> aggregate(Collection<? extends BufferPublicKeyParser<? extends PublicKey>> parsers) {
+ if (GenericUtils.isEmpty(parsers)) {
+ return EMPTY;
+ }
+
+ return new BufferPublicKeyParser<PublicKey>() {
+ @Override
+ public boolean isKeyTypeSupported(String keyType) {
+ for (BufferPublicKeyParser<? extends PublicKey> p : parsers) {
+ if (p.isKeyTypeSupported(keyType)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public PublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
+ for (BufferPublicKeyParser<? extends PublicKey> p : parsers) {
+ if (p.isKeyTypeSupported(keyType)) {
+ return p.getRawPublicKey(keyType, buffer);
+ }
+ }
+
+ throw new NoSuchAlgorithmException("No aggregate matcher for " + keyType);
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(parsers);
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/DSSBufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/DSSBufferPublicKeyParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/DSSBufferPublicKeyParser.java
new file mode 100644
index 0000000..4eec49a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/DSSBufferPublicKeyParser.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.buffer.keys;
+
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DSSBufferPublicKeyParser extends AbstractBufferPublicKeyParser<DSAPublicKey> {
+ public static final DSSBufferPublicKeyParser INSTANCE = new DSSBufferPublicKeyParser();
+
+ public DSSBufferPublicKeyParser() {
+ super(DSAPublicKey.class, KeyPairProvider.SSH_DSS);
+ }
+
+ @Override
+ public DSAPublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
+ ValidateUtils.checkTrue(isKeyTypeSupported(keyType), "Unsupported key type: %s", keyType);
+ BigInteger p = buffer.getMPInt();
+ BigInteger q = buffer.getMPInt();
+ BigInteger g = buffer.getMPInt();
+ BigInteger y = buffer.getMPInt();
+
+ return generatePublicKey(KeyUtils.DSS_ALGORITHM, new DSAPublicKeySpec(y, p, q, g));
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/ECBufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/ECBufferPublicKeyParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/ECBufferPublicKeyParser.java
new file mode 100644
index 0000000..df0115a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/ECBufferPublicKeyParser.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.buffer.keys;
+
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ECBufferPublicKeyParser extends AbstractBufferPublicKeyParser<ECPublicKey> {
+ public static final ECBufferPublicKeyParser INSTANCE = new ECBufferPublicKeyParser();
+
+ public ECBufferPublicKeyParser() {
+ super(ECPublicKey.class, ECCurves.KEY_TYPES);
+ }
+
+ @Override
+ public ECPublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
+ ValidateUtils.checkTrue(isKeyTypeSupported(keyType), "Unsupported key type: %s", keyType);
+ ECCurves curve = ECCurves.fromKeyType(keyType);
+ if (curve == null) {
+ throw new NoSuchAlgorithmException("Unsupported raw public algorithm: " + keyType);
+ }
+
+ String curveName = curve.getName();
+ ECParameterSpec params = curve.getParameters();
+ return getRawECKey(curveName, params, buffer);
+ }
+
+ protected ECPublicKey getRawECKey(String expectedCurve, ECParameterSpec spec, Buffer buffer) throws GeneralSecurityException {
+ String curveName = buffer.getString();
+ if (!expectedCurve.equals(curveName)) {
+ throw new InvalidKeySpecException("getRawECKey(" + expectedCurve + ") curve name does not match expected: " + curveName);
+ }
+
+ if (spec == null) {
+ throw new InvalidKeySpecException("getRawECKey(" + expectedCurve + ") missing curve parameters");
+ }
+
+ byte[] octets = buffer.getBytes();
+ ECPoint w;
+ try {
+ w = ECCurves.octetStringToEcPoint(octets);
+ } catch (RuntimeException e) {
+ throw new InvalidKeySpecException("getRawECKey(" + expectedCurve + ")"
+ + " cannot (" + e.getClass().getSimpleName() + ")"
+ + " retrieve W value: " + e.getMessage(),
+ e);
+ }
+
+ return generatePublicKey(KeyUtils.EC_ALGORITHM, new ECPublicKeySpec(w, spec));
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/ED25519BufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/ED25519BufferPublicKeyParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/ED25519BufferPublicKeyParser.java
new file mode 100644
index 0000000..61ce6ab
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/ED25519BufferPublicKeyParser.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.buffer.keys;
+
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * TODO complete this when SSHD-440 is done
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ED25519BufferPublicKeyParser extends AbstractBufferPublicKeyParser<PublicKey> {
+ public static final ED25519BufferPublicKeyParser INSTANCE = new ED25519BufferPublicKeyParser();
+
+ public ED25519BufferPublicKeyParser() {
+ super(PublicKey.class, KeyPairProvider.SSH_ED25519);
+ }
+
+ @Override
+ public PublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
+ ValidateUtils.checkTrue(isKeyTypeSupported(keyType), "Unsupported key type: %s", keyType);
+ byte[] seed = buffer.getBytes();
+ return SecurityUtils.generateEDDSAPublicKey(keyType, seed);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/RSABufferPublicKeyParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/RSABufferPublicKeyParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/RSABufferPublicKeyParser.java
new file mode 100644
index 0000000..363b07e
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/RSABufferPublicKeyParser.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.buffer.keys;
+
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.RSAPublicKeySpec;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class RSABufferPublicKeyParser extends AbstractBufferPublicKeyParser<RSAPublicKey> {
+ public static final RSABufferPublicKeyParser INSTANCE = new RSABufferPublicKeyParser();
+
+ public RSABufferPublicKeyParser() {
+ super(RSAPublicKey.class, KeyPairProvider.SSH_RSA);
+ }
+
+ @Override
+ public RSAPublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
+ ValidateUtils.checkTrue(isKeyTypeSupported(keyType), "Unsupported key type: %s", keyType);
+ BigInteger e = buffer.getMPInt();
+ BigInteger n = buffer.getMPInt();
+ return generatePublicKey(KeyUtils.RSA_ALGORITHM, new RSAPublicKeySpec(n, e));
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java
new file mode 100644
index 0000000..6413ebb
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/AbstractCloseable.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.closeable;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.future.SshFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+
+/**
+ * Provides some default implementations
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractCloseable extends IoBaseCloseable {
+
+ public enum State {
+ Opened, Graceful, Immediate, Closed
+ }
+
+ /**
+ * Lock object for this session state
+ */
+ protected final Object lock = new Object();
+
+ /**
+ * State of this object
+ */
+ protected final AtomicReference<AbstractCloseable.State> state = new AtomicReference<>(State.Opened);
+
+ /**
+ * A future that will be set 'closed' when the object is actually closed
+ */
+ protected final CloseFuture closeFuture;
+
+ protected AbstractCloseable() {
+ this("");
+ }
+
+ protected AbstractCloseable(String discriminator) {
+ super(discriminator);
+ closeFuture = new DefaultCloseFuture(discriminator, lock);
+ }
+
+ @Override
+ public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ closeFuture.addListener(listener);
+ }
+
+ @Override
+ public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ closeFuture.removeListener(listener);
+ }
+
+ @Override
+ public final CloseFuture close(boolean immediately) {
+ boolean debugEnabled = log.isDebugEnabled();
+ if (immediately) {
+ if (state.compareAndSet(State.Opened, State.Immediate)
+ || state.compareAndSet(State.Graceful, State.Immediate)) {
+ if (debugEnabled) {
+ log.debug("close({}) Closing immediately", this);
+ }
+ preClose();
+ doCloseImmediately();
+ if (debugEnabled) {
+ log.debug("close({})[Immediately] closed", this);
+ }
+ } else {
+ if (debugEnabled) {
+ log.debug("close({})[Immediately] state already {}", this, state.get());
+ }
+ }
+ } else {
+ if (state.compareAndSet(State.Opened, State.Graceful)) {
+ if (debugEnabled) {
+ log.debug("close({}) Closing gracefully", this);
+ }
+ preClose();
+ SshFuture<CloseFuture> grace = doCloseGracefully();
+ if (grace != null) {
+ grace.addListener(future -> {
+ if (state.compareAndSet(State.Graceful, State.Immediate)) {
+ doCloseImmediately();
+ if (debugEnabled) {
+ log.debug("close({}][Graceful] - operationComplete() closed", AbstractCloseable.this);
+ }
+ }
+ });
+ } else {
+ if (state.compareAndSet(State.Graceful, State.Immediate)) {
+ doCloseImmediately();
+ if (debugEnabled) {
+ log.debug("close({})[Graceful] closed", this);
+ }
+ }
+ }
+ } else {
+ if (debugEnabled) {
+ log.debug("close({})[Graceful] state already {}", this, state.get());
+ }
+ }
+ }
+ return closeFuture;
+ }
+
+ @Override
+ public final boolean isClosed() {
+ return state.get() == State.Closed;
+ }
+
+ @Override
+ public final boolean isClosing() {
+ return state.get() != State.Opened;
+ }
+
+ /**
+ * preClose is guaranteed to be called before doCloseGracefully or doCloseImmediately.
+ * When preClose() is called, isClosing() == true
+ */
+ protected void preClose() {
+ // nothing
+ }
+
+ protected CloseFuture doCloseGracefully() {
+ return null;
+ }
+
+ /**
+ * <P>doCloseImmediately is called once and only once
+ * with state == Immediate</P>
+ *
+ * <P>Overriding methods should always call the base implementation.
+ * It may be called concurrently while preClose() or doCloseGracefully is executing</P>
+ */
+ protected void doCloseImmediately() {
+ closeFuture.setClosed();
+ state.set(State.Closed);
+ }
+
+ protected Builder builder() {
+ return new Builder(lock);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/AbstractInnerCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/AbstractInnerCloseable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/AbstractInnerCloseable.java
new file mode 100644
index 0000000..6518d23
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/AbstractInnerCloseable.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.closeable;
+
+import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.future.CloseFuture;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractInnerCloseable extends AbstractCloseable {
+ protected AbstractInnerCloseable() {
+ this("");
+ }
+
+ protected AbstractInnerCloseable(String discriminator) {
+ super(discriminator);
+ }
+
+ protected abstract Closeable getInnerCloseable();
+
+ @Override
+ protected final CloseFuture doCloseGracefully() {
+ return getInnerCloseable().close(false);
+ }
+
+ @Override
+ @SuppressWarnings("synthetic-access")
+ protected final void doCloseImmediately() {
+ getInnerCloseable().close(true).addListener(future -> AbstractInnerCloseable.super.doCloseImmediately());
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/Builder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/Builder.java b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/Builder.java
new file mode 100644
index 0000000..847d49c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/Builder.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.closeable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.future.SshFuture;
+import org.apache.sshd.common.util.ObjectBuilder;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class Builder implements ObjectBuilder<Closeable> {
+ private final Object lock;
+ private final List<Closeable> closeables = new ArrayList<>();
+
+ public Builder(Object lock) {
+ this.lock = Objects.requireNonNull(lock, "No lock");
+ }
+
+ public Builder run(Object id, Runnable r) {
+ return close(new SimpleCloseable(id, lock) {
+ @Override
+ protected void doClose(boolean immediately) {
+ try {
+ r.run();
+ } finally {
+ super.doClose(immediately);
+ }
+ }
+ });
+ }
+
+ @SuppressWarnings("rawtypes")
+ public <T extends SshFuture> Builder when(SshFuture<T> future) {
+ if (future != null) {
+ when(future.getId(), Collections.singleton(future));
+ }
+ return this;
+ }
+
+ @SuppressWarnings("rawtypes")
+ @SafeVarargs
+ public final <T extends SshFuture> Builder when(SshFuture<T>... futures) {
+ return when(getClass().getSimpleName(), Arrays.asList(futures));
+ }
+
+ @SuppressWarnings("rawtypes")
+ public <T extends SshFuture> Builder when(Object id, Iterable<? extends SshFuture<T>> futures) {
+ return close(new FuturesCloseable<>(id, lock, futures));
+ }
+
+ public Builder sequential(Closeable... closeables) {
+ for (Closeable closeable : closeables) {
+ close(closeable);
+ }
+ return this;
+ }
+
+ public Builder sequential(Object id, Iterable<Closeable> closeables) {
+ return close(new SequentialCloseable(id, lock, closeables));
+ }
+
+ public Builder parallel(Closeable... closeables) {
+ if (closeables.length == 1) {
+ close(closeables[0]);
+ } else if (closeables.length > 0) {
+ parallel(getClass().getSimpleName(), Arrays.asList(closeables));
+ }
+ return this;
+ }
+
+ public Builder parallel(Object id, Iterable<? extends Closeable> closeables) {
+ return close(new ParallelCloseable(id, lock, closeables));
+ }
+
+ public Builder close(Closeable c) {
+ if (c != null) {
+ closeables.add(c);
+ }
+ return this;
+ }
+
+ @Override
+ public Closeable build() {
+ if (closeables.isEmpty()) {
+ return new SimpleCloseable(getClass().getSimpleName(), lock);
+ } else if (closeables.size() == 1) {
+ return closeables.get(0);
+ } else {
+ return new SequentialCloseable(getClass().getSimpleName(), lock, closeables);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/FuturesCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/FuturesCloseable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/FuturesCloseable.java
new file mode 100644
index 0000000..af765b7
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/FuturesCloseable.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.closeable;
+
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.future.DefaultSshFuture;
+import org.apache.sshd.common.future.SshFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+
+/**
+ * @param <T> Type of future
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class FuturesCloseable<T extends SshFuture> extends SimpleCloseable {
+
+ private final Iterable<? extends SshFuture<T>> futures;
+
+ public FuturesCloseable(Object id, Object lock, Iterable<? extends SshFuture<T>> futures) {
+ super(id, lock);
+ this.futures = (futures == null) ? Collections.emptyList() : futures;
+ }
+
+ @Override
+ protected void doClose(boolean immediately) {
+ if (immediately) {
+ for (SshFuture<?> f : futures) {
+ if (f instanceof DefaultSshFuture) {
+ ((DefaultSshFuture<?>) f).setValue(new SshException("Closed"));
+ }
+ }
+ future.setClosed();
+ } else {
+ AtomicInteger count = new AtomicInteger(1);
+ boolean traceEnabled = log.isTraceEnabled();
+ SshFutureListener<T> listener = f -> {
+ int pendingCount = count.decrementAndGet();
+ if (traceEnabled) {
+ log.trace("doClose(" + immediately + ") complete pending: " + pendingCount);
+ }
+ if (pendingCount == 0) {
+ future.setClosed();
+ }
+ };
+
+ for (SshFuture<T> f : futures) {
+ if (f != null) {
+ int pendingCount = count.incrementAndGet();
+ if (traceEnabled) {
+ log.trace("doClose(" + immediately + ") future pending: " + pendingCount);
+ }
+ f.addListener(listener);
+ }
+ }
+ listener.operationComplete(null);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java
new file mode 100644
index 0000000..f4c6d1a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/IoBaseCloseable.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.closeable;
+
+import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class IoBaseCloseable extends AbstractLoggingBean implements Closeable {
+ protected IoBaseCloseable() {
+ this("");
+ }
+
+ protected IoBaseCloseable(String discriminator) {
+ super(discriminator);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/ParallelCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/ParallelCloseable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/ParallelCloseable.java
new file mode 100644
index 0000000..0900cd7
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/ParallelCloseable.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.closeable;
+
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+
+/**
+ * Waits for a group of {@link Closeable}s to complete in any order, then
+ * signals the completion by setting the "parent" future as closed
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ParallelCloseable extends SimpleCloseable {
+ private final Iterable<? extends Closeable> closeables;
+
+ public ParallelCloseable(Object id, Object lock, Iterable<? extends Closeable> closeables) {
+ super(id, lock);
+ this.closeables = (closeables == null) ? Collections.emptyList() : closeables;
+ }
+
+ @Override
+ protected void doClose(boolean immediately) {
+ AtomicInteger count = new AtomicInteger(1);
+ boolean traceEnabled = log.isTraceEnabled();
+ SshFutureListener<CloseFuture> listener = f -> {
+ int pendingCount = count.decrementAndGet();
+ if (traceEnabled) {
+ log.trace("doClose(" + immediately + ") completed pending: " + pendingCount);
+ }
+ if (pendingCount == 0) {
+ future.setClosed();
+ }
+ };
+
+ for (Closeable c : closeables) {
+ if (c == null) {
+ continue;
+ }
+
+ int pendingCount = count.incrementAndGet();
+ if (traceEnabled) {
+ log.trace("doClose(" + immediately + ") pending closeables: " + pendingCount);
+ }
+ c.close(immediately).addListener(listener);
+ }
+ /*
+ * Trigger the last "decrementAndGet" so that the future is marked as closed
+ * when last "operationComplete" is invoked (which could be this call...)
+ */
+ listener.operationComplete(null);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java
new file mode 100644
index 0000000..6af51b8
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/SequentialCloseable.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.closeable;
+
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+
+/**
+ * Waits for a group of {@link Closeable}s to complete in the given order, then
+ * signals the completion by setting the "parent" future as closed
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SequentialCloseable extends SimpleCloseable {
+ private final Iterable<? extends Closeable> closeables;
+
+ public SequentialCloseable(Object id, Object lock, Iterable<? extends Closeable> closeables) {
+ super(id, lock);
+ this.closeables = (closeables == null) ? Collections.emptyList() : closeables;
+ }
+
+ @Override
+ protected void doClose(boolean immediately) {
+ Iterator<? extends Closeable> iterator = closeables.iterator();
+ SshFutureListener<CloseFuture> listener = new SshFutureListener<CloseFuture>() {
+ @SuppressWarnings("synthetic-access")
+ @Override
+ public void operationComplete(CloseFuture previousFuture) {
+ boolean traceEnabled = log.isTraceEnabled();
+ while (iterator.hasNext()) {
+ Closeable c = iterator.next();
+ if (c != null) {
+ if (traceEnabled) {
+ log.trace("doClose(" + immediately + ") closing " + c);
+ }
+ CloseFuture nextFuture = c.close(immediately);
+ nextFuture.addListener(this);
+ return;
+ }
+ }
+ if (!iterator.hasNext()) {
+ if (log.isDebugEnabled()) {
+ log.debug("doClose(" + immediately + ") signal close complete");
+ }
+ future.setClosed();
+ }
+ }
+ };
+ listener.operationComplete(null);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java
new file mode 100644
index 0000000..e360f13
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/closeable/SimpleCloseable.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.closeable;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SimpleCloseable extends IoBaseCloseable {
+
+ protected final DefaultCloseFuture future;
+ protected final AtomicBoolean closing;
+
+ public SimpleCloseable(Object id, Object lock) {
+ future = new DefaultCloseFuture(id, lock);
+ closing = new AtomicBoolean(false);
+ }
+
+ @Override
+ public boolean isClosed() {
+ return future.isClosed();
+ }
+
+ @Override
+ public boolean isClosing() {
+ return closing.get();
+ }
+
+ @Override
+ public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ future.addListener(listener);
+ }
+
+ @Override
+ public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ future.removeListener(listener);
+ }
+
+ @Override
+ public CloseFuture close(boolean immediately) {
+ if (closing.compareAndSet(false, true)) {
+ doClose(immediately);
+ }
+ return future;
+ }
+
+ protected void doClose(boolean immediately) {
+ future.setClosed();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java
new file mode 100644
index 0000000..c9eca70
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/CloseableEmptyInputStream.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.IOException;
+import java.nio.channels.Channel;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A {@code /dev/null} stream that can be closed - in which case it will throw
+ * {@link IOException}s if invoked after being closed
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class CloseableEmptyInputStream extends EmptyInputStream implements Channel {
+ private final AtomicBoolean open = new AtomicBoolean(true);
+
+ public CloseableEmptyInputStream() {
+ super();
+ }
+
+ @Override
+ public boolean isOpen() {
+ return open.get();
+ }
+
+ @Override
+ public int available() throws IOException {
+ if (isOpen()) {
+ return super.available();
+ } else {
+ throw new IOException("available() stream is closed");
+ }
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (isOpen()) {
+ return super.read();
+ } else {
+ throw new IOException("read() stream is closed");
+ }
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (isOpen()) {
+ return super.read(b, off, len);
+ } else {
+ throw new IOException("read([])[" + off + "," + len + "] stream is closed");
+ }
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ if (isOpen()) {
+ return super.skip(n);
+ } else {
+ throw new IOException("skip(" + n + ") stream is closed");
+ }
+ }
+
+ @Override
+ public synchronized void reset() throws IOException {
+ if (isOpen()) {
+ super.reset();
+ } else {
+ throw new IOException("reset() stream is closed");
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (open.getAndSet(false)) {
+ //noinspection UnnecessaryReturnStatement
+ return; // debug breakpoint
+ }
+ }
+}
[44/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
new file mode 100644
index 0000000..397a007
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/ECDSAPublicKeyEntryDecoder.java
@@ -0,0 +1,178 @@
+/*
+ * 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.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchProviderException;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Objects;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ECDSAPublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<ECPublicKey, ECPrivateKey> {
+ public static final ECDSAPublicKeyEntryDecoder INSTANCE = new ECDSAPublicKeyEntryDecoder();
+
+ // see rfc5480 section 2.2
+ public static final byte ECPOINT_UNCOMPRESSED_FORM_INDICATOR = 0x04;
+ public static final byte ECPOINT_COMPRESSED_VARIANT_2 = 0x02;
+ public static final byte ECPOINT_COMPRESSED_VARIANT_3 = 0x02;
+
+ public ECDSAPublicKeyEntryDecoder() {
+ super(ECPublicKey.class, ECPrivateKey.class, ECCurves.KEY_TYPES);
+ }
+
+ @Override
+ public ECPublicKey decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException {
+ ECCurves curve = ECCurves.fromKeyType(keyType);
+ if (curve == null) {
+ throw new InvalidKeySpecException("Not an EC curve name: " + keyType);
+ }
+
+ if (!SecurityUtils.isECCSupported()) {
+ throw new NoSuchProviderException("ECC not supported");
+ }
+
+ String keyCurveName = curve.getName();
+ // see rfc5656 section 3.1
+ String encCurveName = KeyEntryResolver.decodeString(keyData);
+ if (!keyCurveName.equals(encCurveName)) {
+ throw new InvalidKeySpecException("Mismatched key curve name (" + keyCurveName + ") vs. encoded one (" + encCurveName + ")");
+ }
+
+ byte[] octets = KeyEntryResolver.readRLEBytes(keyData);
+ ECPoint w;
+ try {
+ w = ECCurves.octetStringToEcPoint(octets);
+ if (w == null) {
+ throw new InvalidKeySpecException("No ECPoint generated for curve=" + keyCurveName
+ + " from octets=" + BufferUtils.toHex(':', octets));
+ }
+ } catch (RuntimeException e) {
+ throw new InvalidKeySpecException("Failed (" + e.getClass().getSimpleName() + ")"
+ + " to generate ECPoint for curve=" + keyCurveName
+ + " from octets=" + BufferUtils.toHex(':', octets)
+ + ": " + e.getMessage());
+ }
+
+ ECParameterSpec paramSpec = curve.getParameters();
+ return generatePublicKey(new ECPublicKeySpec(w, paramSpec));
+ }
+
+ @Override
+ public ECPublicKey clonePublicKey(ECPublicKey key) throws GeneralSecurityException {
+ if (!SecurityUtils.isECCSupported()) {
+ throw new NoSuchProviderException("ECC not supported");
+ }
+
+ if (key == null) {
+ return null;
+ }
+
+ ECParameterSpec params = key.getParams();
+ if (params == null) {
+ throw new InvalidKeyException("Missing parameters in key");
+ }
+
+ return generatePublicKey(new ECPublicKeySpec(key.getW(), params));
+ }
+
+ @Override
+ public ECPrivateKey clonePrivateKey(ECPrivateKey key) throws GeneralSecurityException {
+ if (!SecurityUtils.isECCSupported()) {
+ throw new NoSuchProviderException("ECC not supported");
+ }
+
+ if (key == null) {
+ return null;
+ }
+
+ ECParameterSpec params = key.getParams();
+ if (params == null) {
+ throw new InvalidKeyException("Missing parameters in key");
+ }
+
+ return generatePrivateKey(new ECPrivateKeySpec(key.getS(), params));
+ }
+
+ @Override
+ public String encodePublicKey(OutputStream s, ECPublicKey key) throws IOException {
+ Objects.requireNonNull(key, "No public key provided");
+
+ ECParameterSpec params = Objects.requireNonNull(key.getParams(), "No EC parameters available");
+ ECCurves curve = Objects.requireNonNull(ECCurves.fromCurveParameters(params), "Cannot determine curve");
+ String keyType = curve.getKeyType();
+ String curveName = curve.getName();
+ KeyEntryResolver.encodeString(s, keyType);
+ // see rfc5656 section 3.1
+ KeyEntryResolver.encodeString(s, curveName);
+ ECCurves.ECPointCompression.UNCOMPRESSED.writeECPoint(s, curveName, key.getW());
+ return keyType;
+ }
+
+ @Override
+ public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+ if (SecurityUtils.isECCSupported()) {
+ return SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
+ } else {
+ throw new NoSuchProviderException("ECC not supported");
+ }
+ }
+
+ @Override
+ public KeyPair generateKeyPair(int keySize) throws GeneralSecurityException {
+ ECCurves curve = ECCurves.fromCurveSize(keySize);
+ if (curve == null) {
+ throw new InvalidKeySpecException("Unknown curve for key size=" + keySize);
+ }
+
+ KeyPairGenerator gen = getKeyPairGenerator();
+ gen.initialize(curve.getParameters());
+ return gen.generateKeyPair();
+ }
+
+ @Override
+ public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+ if (SecurityUtils.isECCSupported()) {
+ return SecurityUtils.getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
+ } else {
+ throw new NoSuchProviderException("ECC not supported");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java
new file mode 100644
index 0000000..4550815
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/RSAPublicKeyDecoder.java
@@ -0,0 +1,117 @@
+/*
+ * 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.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class RSAPublicKeyDecoder extends AbstractPublicKeyEntryDecoder<RSAPublicKey, RSAPrivateKey> {
+ public static final RSAPublicKeyDecoder INSTANCE = new RSAPublicKeyDecoder();
+
+ public RSAPublicKeyDecoder() {
+ super(RSAPublicKey.class, RSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_RSA)));
+ }
+
+ @Override
+ public RSAPublicKey decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException {
+ if (!KeyPairProvider.SSH_RSA.equals(keyType)) { // just in case we were invoked directly
+ throw new InvalidKeySpecException("Unexpected key type: " + keyType);
+ }
+
+ BigInteger e = KeyEntryResolver.decodeBigInt(keyData);
+ BigInteger n = KeyEntryResolver.decodeBigInt(keyData);
+
+ return generatePublicKey(new RSAPublicKeySpec(n, e));
+ }
+
+ @Override
+ public String encodePublicKey(OutputStream s, RSAPublicKey key) throws IOException {
+ Objects.requireNonNull(key, "No public key provided");
+ KeyEntryResolver.encodeString(s, KeyPairProvider.SSH_RSA);
+ KeyEntryResolver.encodeBigInt(s, key.getPublicExponent());
+ KeyEntryResolver.encodeBigInt(s, key.getModulus());
+
+ return KeyPairProvider.SSH_RSA;
+ }
+
+ @Override
+ public RSAPublicKey clonePublicKey(RSAPublicKey key) throws GeneralSecurityException {
+ if (key == null) {
+ return null;
+ } else {
+ return generatePublicKey(new RSAPublicKeySpec(key.getModulus(), key.getPublicExponent()));
+ }
+ }
+
+ @Override
+ public RSAPrivateKey clonePrivateKey(RSAPrivateKey key) throws GeneralSecurityException {
+ if (key == null) {
+ return null;
+ }
+
+ if (!(key instanceof RSAPrivateCrtKey)) {
+ throw new InvalidKeyException("Cannot clone a non-RSAPrivateCrtKey: " + key.getClass().getSimpleName());
+ }
+
+ RSAPrivateCrtKey rsaPrv = (RSAPrivateCrtKey) key;
+ return generatePrivateKey(
+ new RSAPrivateCrtKeySpec(
+ rsaPrv.getModulus(),
+ rsaPrv.getPublicExponent(),
+ rsaPrv.getPrivateExponent(),
+ rsaPrv.getPrimeP(),
+ rsaPrv.getPrimeQ(),
+ rsaPrv.getPrimeExponentP(),
+ rsaPrv.getPrimeExponentQ(),
+ rsaPrv.getCrtCoefficient()));
+ }
+
+ @Override
+ public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+ return SecurityUtils.getKeyPairGenerator(KeyUtils.RSA_ALGORITHM);
+ }
+
+ @Override
+ public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+ return SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java
new file mode 100644
index 0000000..4437945
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java
@@ -0,0 +1,110 @@
+/*
+ * 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.loader;
+
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.spec.InvalidKeySpecException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.crypto.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 AESPrivateKeyObfuscator extends AbstractPrivateKeyObfuscator {
+ public static final String CIPHER_NAME = "AES";
+ public static final AESPrivateKeyObfuscator INSTANCE = new AESPrivateKeyObfuscator();
+
+ public AESPrivateKeyObfuscator() {
+ super(CIPHER_NAME);
+ }
+
+ @Override
+ public List<Integer> getSupportedKeySizes() {
+ return getAvailableKeyLengths();
+ }
+
+ @Override
+ public byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException {
+ int keyLength = resolveKeyLength(encContext);
+ byte[] keyValue = deriveEncryptionKey(encContext, keyLength / Byte.SIZE);
+ return applyPrivateKeyCipher(bytes, encContext, keyLength, keyValue, encryptIt);
+ }
+
+ @Override
+ protected int resolveKeyLength(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException {
+ String cipherType = encContext.getCipherType();
+ try {
+ int keyLength = Integer.parseInt(cipherType);
+ List<Integer> sizes = getSupportedKeySizes();
+ for (Integer s : sizes) {
+ if (s.intValue() == keyLength) {
+ return keyLength;
+ }
+ }
+
+ throw new InvalidKeySpecException("Unknown " + getCipherName() + " key length: " + cipherType + " - supported: " + sizes);
+ } catch (NumberFormatException e) {
+ throw new InvalidKeySpecException("Bad " + getCipherName() + " key length (" + cipherType + "): " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * @return A {@link List} of {@link Integer}s holding the available key
+ * lengths values (in bits) for the JVM. <B>Note:</B> AES 256 requires
+ * special JCE policy extension installation (e.g., for Java 7 see
+ * <A HREF="http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html">this link</A>)
+ */
+ @SuppressWarnings("synthetic-access")
+ public static List<Integer> getAvailableKeyLengths() {
+ return LazyKeyLengthsHolder.KEY_LENGTHS;
+ }
+
+ private static final class LazyKeyLengthsHolder {
+ private static final List<Integer> KEY_LENGTHS = Collections.unmodifiableList(detectSupportedKeySizes());
+
+ private LazyKeyLengthsHolder() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+
+ // AES 256 requires special JCE policy extension installation
+ private static List<Integer> detectSupportedKeySizes() {
+ List<Integer> sizes = new ArrayList<>();
+ for (int keyLength = 128; keyLength < Short.MAX_VALUE /* just so it doesn't go forever */; keyLength += 64) {
+ try {
+ byte[] keyAsBytes = new byte[keyLength / Byte.SIZE];
+ Key key = new SecretKeySpec(keyAsBytes, CIPHER_NAME);
+ Cipher c = SecurityUtils.getCipher(CIPHER_NAME);
+ c.init(Cipher.DECRYPT_MODE, key);
+ sizes.add(Integer.valueOf(keyLength));
+ } catch (GeneralSecurityException e) {
+ return sizes;
+ }
+ }
+
+ throw new IllegalStateException("No limit encountered: " + sizes);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
new file mode 100644
index 0000000..a83bf68
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
@@ -0,0 +1,181 @@
+/*
+ * 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.loader;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractKeyPairResourceParser extends AbstractLoggingBean implements KeyPairResourceParser {
+ private final List<String> beginners;
+ private final List<String> enders;
+ private final List<List<String>> endingMarkers;
+
+ /**
+ * @param beginners The markers that indicate the beginning of a parsing block
+ * @param enders The <U>matching</U> (by position) markers that indicate the end of a parsing block
+ */
+ protected AbstractKeyPairResourceParser(List<String> beginners, List<String> enders) {
+ this.beginners = ValidateUtils.checkNotNullAndNotEmpty(beginners, "No begin markers");
+ this.enders = ValidateUtils.checkNotNullAndNotEmpty(enders, "No end markers");
+ ValidateUtils.checkTrue(
+ beginners.size() == enders.size(), "Mismatched begin(%d)/end(%d) markers sizes", beginners.size(), enders.size());
+ endingMarkers = new ArrayList<>(enders.size());
+ enders.forEach(m -> endingMarkers.add(Collections.singletonList(m)));
+ }
+
+ public List<String> getBeginners() {
+ return beginners;
+ }
+
+ public List<String> getEnders() {
+ return enders;
+ }
+
+ /**
+ * @return A {@link List} of same size as the ending markers, where
+ * each ending marker is encapsulated inside a singleton list and
+ * resides as the <U>same index</U> as the marker it encapsulates
+ */
+ public List<List<String>> getEndingMarkers() {
+ return endingMarkers;
+ }
+
+ @Override
+ public boolean canExtractKeyPairs(String resourceKey, List<String> lines) throws IOException, GeneralSecurityException {
+ return KeyPairResourceParser.containsMarkerLine(lines, getBeginners());
+ }
+
+ @Override
+ public Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+ throws IOException, GeneralSecurityException {
+ Collection<KeyPair> keyPairs = Collections.emptyList();
+ List<String> beginMarkers = getBeginners();
+ List<List<String>> endMarkers = getEndingMarkers();
+ for (Map.Entry<Integer, Integer> markerPos = KeyPairResourceParser.findMarkerLine(lines, beginMarkers); markerPos != null;) {
+ int startIndex = markerPos.getKey();
+ String startLine = lines.get(startIndex);
+ startIndex++;
+
+ int markerIndex = markerPos.getValue();
+ List<String> ender = endMarkers.get(markerIndex);
+ markerPos = KeyPairResourceParser.findMarkerLine(lines, startIndex, ender);
+ if (markerPos == null) {
+ throw new StreamCorruptedException("Missing end marker (" + ender + ") after line #" + startIndex);
+ }
+
+ int endIndex = markerPos.getKey();
+ String endLine = lines.get(endIndex);
+ Collection<KeyPair> kps =
+ extractKeyPairs(resourceKey, startLine, endLine, passwordProvider, lines.subList(startIndex, endIndex));
+ if (GenericUtils.isNotEmpty(kps)) {
+ if (GenericUtils.isEmpty(keyPairs)) {
+ keyPairs = new LinkedList<>(kps);
+ } else {
+ keyPairs.addAll(kps);
+ }
+ }
+
+ // see if there are more
+ markerPos = KeyPairResourceParser.findMarkerLine(lines, endIndex + 1, beginMarkers);
+ }
+
+ return keyPairs;
+ }
+
+ /**
+ * Extracts the key pairs within a <U>single</U> delimited by markers block of lines. By
+ * default cleans up the empty lines, joins them and converts them from BASE64
+ *
+ * @param resourceKey A hint as to the origin of the text lines
+ * @param beginMarker The line containing the begin marker
+ * @param endMarker The line containing the end marker
+ * @param passwordProvider The {@link FilePasswordProvider} to use
+ * in case the data is encrypted - may be {@code null} if no encrypted
+ * @param lines The block of lines between the markers
+ * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
+ * @throws IOException If failed to parse the data
+ * @throws GeneralSecurityException If failed to generate the keys
+ * @see #extractKeyPairs(String, String, String, FilePasswordProvider, byte[])
+ */
+ public Collection<KeyPair> extractKeyPairs(
+ String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
+ throws IOException, GeneralSecurityException {
+ return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, KeyPairResourceParser.extractDataBytes(lines));
+ }
+
+ /**
+ * @param resourceKey A hint as to the origin of the text lines
+ * @param beginMarker The line containing the begin marker
+ * @param endMarker The line containing the end marker
+ * @param passwordProvider The {@link FilePasswordProvider} to use
+ * in case the data is encrypted - may be {@code null} if no encrypted
+ * @param bytes The decoded bytes from the lines containing the data
+ * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
+ * @throws IOException If failed to parse the data
+ * @throws GeneralSecurityException If failed to generate the keys
+ * @see #extractKeyPairs(String, String, String, FilePasswordProvider, InputStream)
+ */
+ public Collection<KeyPair> extractKeyPairs(
+ String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, byte[] bytes)
+ throws IOException, GeneralSecurityException {
+ if (log.isTraceEnabled()) {
+ BufferUtils.dumpHex(getSimplifiedLogger(), Level.FINER, beginMarker, ':', 16, bytes);
+ }
+
+ try (InputStream bais = new ByteArrayInputStream(bytes)) {
+ return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, bais);
+ }
+ }
+
+ /**
+ * @param resourceKey A hint as to the origin of the text lines
+ * @param beginMarker The line containing the begin marker
+ * @param endMarker The line containing the end marker
+ * @param passwordProvider The {@link FilePasswordProvider} to use
+ * in case the data is encrypted - may be {@code null} if no encrypted
+ * @param stream The decoded data {@link InputStream}
+ * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
+ * @throws IOException If failed to parse the data
+ * @throws GeneralSecurityException If failed to generate the keys
+ */
+ public abstract Collection<KeyPair> extractKeyPairs(
+ String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+ throws IOException, GeneralSecurityException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java
new file mode 100644
index 0000000..ac93ab4
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java
@@ -0,0 +1,191 @@
+/*
+ * 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.loader;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Random;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.digest.BuiltinDigests;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractPrivateKeyObfuscator implements PrivateKeyObfuscator {
+ private final String algName;
+
+ protected AbstractPrivateKeyObfuscator(String name) {
+ algName = ValidateUtils.checkNotNullAndNotEmpty(name, "No name specified");
+ }
+
+ @Override
+ public final String getCipherName() {
+ return algName;
+ }
+
+ @Override
+ public byte[] generateInitializationVector(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException {
+ return generateInitializationVector(resolveKeyLength(encContext));
+ }
+
+ @Override
+ public <A extends Appendable> A appendPrivateKeyEncryptionContext(A sb, PrivateKeyEncryptionContext encContext) throws IOException {
+ if (encContext == null) {
+ return sb;
+ }
+
+ sb.append("DEK-Info: ").append(encContext.getCipherName())
+ .append('-').append(encContext.getCipherType())
+ .append('-').append(encContext.getCipherMode());
+
+ byte[] initVector = encContext.getInitVector();
+ Objects.requireNonNull(initVector, "No encryption init vector");
+ ValidateUtils.checkTrue(initVector.length > 0, "Empty encryption init vector");
+ BufferUtils.appendHex(sb.append(','), BufferUtils.EMPTY_HEX_SEPARATOR, initVector);
+ sb.append(System.lineSeparator());
+ return sb;
+ }
+
+ protected byte[] generateInitializationVector(int keyLength) {
+ int keySize = keyLength / Byte.SIZE;
+ if ((keyLength % Byte.SIZE) != 0) { // e.g., if 36-bits then we need 5 bytes to hold
+ keySize++;
+ }
+
+ byte[] initVector = new byte[keySize];
+ Random randomizer = new SecureRandom(); // TODO consider using some pre-created singleton instance
+ randomizer.nextBytes(initVector);
+ return initVector;
+ }
+
+ protected abstract int resolveKeyLength(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException;
+
+ // see http://martin.kleppmann.com/2013/05/24/improving-security-of-ssh-private-keys.html
+ // see http://www.ict.griffith.edu.au/anthony/info/crypto/openssl.hints (Password to Encryption Key section)
+ // see http://openssl.6102.n7.nabble.com/DES-EDE3-CBC-technical-details-td24883.html
+ protected byte[] deriveEncryptionKey(PrivateKeyEncryptionContext encContext, int outputKeyLength) throws GeneralSecurityException {
+ Objects.requireNonNull(encContext, "No encryption context");
+ ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherName(), "No cipher name");
+ ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherType(), "No cipher type");
+ ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherMode(), "No cipher mode");
+
+ byte[] initVector = Objects.requireNonNull(encContext.getInitVector(), "No encryption init vector");
+ ValidateUtils.checkTrue(initVector.length > 0, "Empty encryption init vector");
+
+ String password = ValidateUtils.checkNotNullAndNotEmpty(encContext.getPassword(), "No encryption password");
+ byte[] passBytes = password.getBytes(StandardCharsets.UTF_8);
+ byte[] keyValue = new byte[outputKeyLength];
+ MessageDigest hash = SecurityUtils.getMessageDigest(BuiltinDigests.Constants.MD5);
+ byte[] prevHash = GenericUtils.EMPTY_BYTE_ARRAY;
+ for (int index = 0, remLen = keyValue.length; index < keyValue.length;) {
+ hash.reset(); // just making sure
+
+ hash.update(prevHash, 0, prevHash.length);
+ hash.update(passBytes, 0, passBytes.length);
+ hash.update(initVector, 0, Math.min(initVector.length, 8));
+
+ prevHash = hash.digest();
+
+ System.arraycopy(prevHash, 0, keyValue, index, Math.min(remLen, prevHash.length));
+ index += prevHash.length;
+ remLen -= prevHash.length;
+ }
+
+ return keyValue;
+ }
+
+ protected byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, int numBits, byte[] keyValue, boolean encryptIt)
+ throws GeneralSecurityException {
+ Objects.requireNonNull(encContext, "No encryption context");
+ String cipherName = ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherName(), "No cipher name");
+ ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherType(), "No cipher type");
+ String cipherMode = ValidateUtils.checkNotNullAndNotEmpty(encContext.getCipherMode(), "No cipher mode");
+
+ Objects.requireNonNull(bytes, "No source data");
+ Objects.requireNonNull(keyValue, "No encryption key");
+ ValidateUtils.checkTrue(keyValue.length > 0, "Empty encryption key");
+
+ byte[] initVector = Objects.requireNonNull(encContext.getInitVector(), "No encryption init vector");
+ ValidateUtils.checkTrue(initVector.length > 0, "Empty encryption init vector");
+
+ String xform = cipherName + "/" + cipherMode + "/NoPadding";
+ int maxAllowedBits = Cipher.getMaxAllowedKeyLength(xform);
+ // see http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
+ if (numBits > maxAllowedBits) {
+ throw new InvalidKeySpecException("applyPrivateKeyCipher(" + xform + ")[encrypt=" + encryptIt + "]"
+ + " required key length (" + numBits + ")"
+ + " exceeds max. available: " + maxAllowedBits);
+ }
+
+ SecretKeySpec skeySpec = new SecretKeySpec(keyValue, cipherName);
+ IvParameterSpec ivspec = new IvParameterSpec(initVector);
+ Cipher cipher = SecurityUtils.getCipher(xform);
+ int blockSize = cipher.getBlockSize();
+ int dataSize = bytes.length;
+ cipher.init(encryptIt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, skeySpec, ivspec);
+ if (blockSize <= 0) {
+ return cipher.doFinal(bytes);
+ }
+
+ int remLen = dataSize % blockSize;
+ if (remLen <= 0) {
+ return cipher.doFinal(bytes);
+ }
+
+ int updateSize = dataSize - remLen;
+ byte[] lastBlock = new byte[blockSize];
+ Arrays.fill(lastBlock, (byte) 10);
+ System.arraycopy(bytes, updateSize, lastBlock, 0, remLen);
+
+ // TODO for some reason, calling cipher.update followed by cipher.doFinal does not work
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(dataSize);
+ try {
+ try {
+ byte[] buf = cipher.update(bytes, 0, updateSize);
+ baos.write(buf);
+
+ buf = cipher.doFinal(lastBlock);
+ baos.write(buf);
+ } finally {
+ baos.close();
+ }
+ } catch (IOException e) {
+ throw new GeneralSecurityException("applyPrivateKeyCipher(" + xform + ")[encrypt=" + encryptIt + "]"
+ + " failed (" + e.getClass().getSimpleName() + ")"
+ + " to split-write: " + e.getMessage(), e);
+ }
+
+ return baos.toByteArray();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java
new file mode 100644
index 0000000..2043f06
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java
@@ -0,0 +1,76 @@
+/*
+ * 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.loader;
+
+import java.security.GeneralSecurityException;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DESPrivateKeyObfuscator extends AbstractPrivateKeyObfuscator {
+ public static final int DEFAULT_KEY_LENGTH = 24 /* hardwired size for 3DES */;
+ public static final List<Integer> AVAILABLE_KEY_LENGTHS =
+ Collections.unmodifiableList(Collections.singletonList(Integer.valueOf(DEFAULT_KEY_LENGTH)));
+ public static final DESPrivateKeyObfuscator INSTANCE = new DESPrivateKeyObfuscator();
+
+ public DESPrivateKeyObfuscator() {
+ super("DES");
+ }
+
+ @Override
+ public byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException {
+ PrivateKeyEncryptionContext effContext = resolveEffectiveContext(encContext);
+ byte[] keyValue = deriveEncryptionKey(effContext, DEFAULT_KEY_LENGTH);
+ return applyPrivateKeyCipher(bytes, effContext, keyValue.length * Byte.SIZE, keyValue, encryptIt);
+ }
+
+ @Override
+ public List<Integer> getSupportedKeySizes() {
+ return AVAILABLE_KEY_LENGTHS;
+ }
+
+ @Override
+ protected int resolveKeyLength(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException {
+ return DEFAULT_KEY_LENGTH;
+ }
+
+ @Override
+ protected byte[] generateInitializationVector(int keyLength) {
+ return super.generateInitializationVector(8 * Byte.SIZE);
+ }
+
+ public static final PrivateKeyEncryptionContext resolveEffectiveContext(PrivateKeyEncryptionContext encContext) {
+ if (encContext == null) {
+ return null;
+ }
+
+ String cipherName = encContext.getCipherName();
+ String cipherType = encContext.getCipherType();
+ PrivateKeyEncryptionContext effContext = encContext;
+ if ("EDE3".equalsIgnoreCase(cipherType)) {
+ cipherName += "ede";
+ effContext = encContext.clone();
+ effContext.setCipherName(cipherName);
+ }
+
+ return effContext;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
new file mode 100644
index 0000000..fa6930a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
@@ -0,0 +1,129 @@
+/*
+ * 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.loader;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.net.URL;
+import java.nio.charset.Charset;
+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.KeyPair;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.util.io.IoUtils;
+
+/**
+ * Loads {@link KeyPair}s from text resources
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface KeyPairResourceLoader {
+ /**
+ * An empty loader that never fails but always returns an empty list
+ */
+ KeyPairResourceLoader EMPTY = (resourceKey, passwordProvider, lines) -> Collections.emptyList();
+
+ default Collection<KeyPair> loadKeyPairs(Path path, FilePasswordProvider passwordProvider, OpenOption... options)
+ throws IOException, GeneralSecurityException {
+ return loadKeyPairs(path, passwordProvider, StandardCharsets.UTF_8, options);
+ }
+
+ default Collection<KeyPair> loadKeyPairs(Path path, FilePasswordProvider passwordProvider, Charset cs, OpenOption... options)
+ throws IOException, GeneralSecurityException {
+ try (InputStream stream = Files.newInputStream(path, options)) {
+ return loadKeyPairs(path.toString(), passwordProvider, stream, cs);
+ }
+ }
+
+ default Collection<KeyPair> loadKeyPairs(URL url, FilePasswordProvider passwordProvider)
+ throws IOException, GeneralSecurityException {
+ return loadKeyPairs(url, passwordProvider, StandardCharsets.UTF_8);
+ }
+
+ default Collection<KeyPair> loadKeyPairs(URL url, FilePasswordProvider passwordProvider, Charset cs)
+ throws IOException, GeneralSecurityException {
+ try (InputStream stream = Objects.requireNonNull(url, "No URL").openStream()) {
+ return loadKeyPairs(url.toExternalForm(), passwordProvider, stream, cs);
+ }
+ }
+
+ default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, String data)
+ throws IOException, GeneralSecurityException {
+ try (Reader reader = new StringReader((data == null) ? "" : data)) {
+ return loadKeyPairs(resourceKey, passwordProvider, reader);
+ }
+ }
+
+ default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, InputStream stream)
+ throws IOException, GeneralSecurityException {
+ return loadKeyPairs(resourceKey, passwordProvider, stream, StandardCharsets.UTF_8);
+ }
+
+ default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, InputStream stream, Charset cs)
+ throws IOException, GeneralSecurityException {
+ try (Reader reader = new InputStreamReader(
+ Objects.requireNonNull(stream, "No stream instance"), Objects.requireNonNull(cs, "No charset"))) {
+ return loadKeyPairs(resourceKey, passwordProvider, reader);
+ }
+ }
+
+ default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, Reader r)
+ throws IOException, GeneralSecurityException {
+ try (BufferedReader br = new BufferedReader(Objects.requireNonNull(r, "No reader instance"), IoUtils.DEFAULT_COPY_SIZE)) {
+ return loadKeyPairs(resourceKey, passwordProvider, br);
+ }
+ }
+
+ default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, BufferedReader r)
+ throws IOException, GeneralSecurityException {
+ return loadKeyPairs(resourceKey, passwordProvider, IoUtils.readAllLines(r));
+ }
+
+ /**
+ * Loads key pairs from the given resource text lines
+ *
+ * @param resourceKey A hint as to the origin of the text lines
+ * @param passwordProvider The {@link FilePasswordProvider} to use
+ * in case the data is encrypted - may be {@code null} if no encrypted
+ * data is expected
+ * @param lines The {@link List} of lines as read from the resource
+ * @return The extracted {@link KeyPair}s - may be {@code null}/empty if none.
+ * <B>Note:</B> the resource loader may decide to skip unknown lines if
+ * more than one key pair type is encoded in it
+ * @throws IOException If failed to process the lines
+ * @throws GeneralSecurityException If failed to generate the keys from the
+ * parsed data
+ */
+ Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+ throws IOException, GeneralSecurityException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
new file mode 100644
index 0000000..80fc2c5
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
@@ -0,0 +1,192 @@
+/*
+ * 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.loader;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+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 interface KeyPairResourceParser extends KeyPairResourceLoader {
+ /**
+ * An empty parser that never fails, but always report that it cannot
+ * extract key pairs and returns empty list if asked to load
+ */
+ KeyPairResourceParser EMPTY = new KeyPairResourceParser() {
+ @Override
+ public Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+ throws IOException, GeneralSecurityException {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean canExtractKeyPairs(String resourceKey, List<String> lines) throws IOException, GeneralSecurityException {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "EMPTY";
+ }
+ };
+
+ /**
+ * @param resourceKey A hint as to the origin of the text lines
+ * @param lines The resource lines
+ * @return {@code true} if the parser can extract some key pairs from the lines
+ * @throws IOException If failed to process the lines
+ * @throws GeneralSecurityException If failed to extract information regarding
+ * the possibility to extract the key pairs
+ */
+ boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+ throws IOException, GeneralSecurityException;
+
+ /**
+ * Converts the lines assumed to contain BASE-64 encoded data into
+ * the actual content bytes.
+ *
+ * @param lines The data lines - empty lines and spaces are automatically
+ * deleted <U>before</U> BASE-64 decoding takes place.
+ * @return The decoded data bytes
+ * @see #joinDataLines(Collection)
+ */
+ static byte[] extractDataBytes(Collection<String> lines) {
+ String data = joinDataLines(lines);
+ Base64.Decoder decoder = Base64.getDecoder();
+ return decoder.decode(data);
+ }
+
+ static String joinDataLines(Collection<String> lines) {
+ String data = GenericUtils.join(lines, ' ');
+ data = data.replaceAll("\\s", "");
+ data = data.trim();
+ return data;
+ }
+
+ static boolean containsMarkerLine(List<String> lines, String marker) {
+ return containsMarkerLine(lines, Collections.singletonList(ValidateUtils.checkNotNullAndNotEmpty(marker, "No marker")));
+ }
+
+ static boolean containsMarkerLine(List<String> lines, List<String> markers) {
+ return findMarkerLine(lines, markers) != null;
+ }
+
+ /**
+ * Attempts to locate a line that contains one of the markers
+ *
+ * @param lines The list of lines to scan - ignored if {@code null}/empty
+ * @param markers The markers to match - ignored if {@code null}/empty
+ * @return A {@link SimpleImmutableEntry} whose key is the <U>first</U> line index
+ * that matched and value the matched marker index - {@code null} if no match found
+ * @see #findMarkerLine(List, int, List)
+ */
+ static SimpleImmutableEntry<Integer, Integer> findMarkerLine(List<String> lines, List<String> markers) {
+ return findMarkerLine(lines, 0, markers);
+ }
+
+ /**
+ * Attempts to locate a line that contains one of the markers
+ *
+ * @param lines The list of lines to scan - ignored if {@code null}/empty
+ * @param startLine The scan start line index
+ * @param markers The markers to match - ignored if {@code null}/empty
+ * @return A {@link SimpleImmutableEntry} whose key is the <U>first</U> line index
+ * that matched and value the matched marker index - {@code null} if no match found
+ */
+ static SimpleImmutableEntry<Integer, Integer> findMarkerLine(List<String> lines, int startLine, List<String> markers) {
+ if (GenericUtils.isEmpty(lines) || GenericUtils.isEmpty(markers)) {
+ return null;
+ }
+
+ for (int lineIndex = startLine; lineIndex < lines.size(); lineIndex++) {
+ String l = lines.get(lineIndex);
+ for (int markerIndex = 0; markerIndex < markers.size(); markerIndex++) {
+ String m = markers.get(markerIndex);
+ if (l.contains(m)) {
+ return new SimpleImmutableEntry<>(lineIndex, markerIndex);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ static KeyPairResourceParser aggregate(KeyPairResourceParser... parsers) {
+ return aggregate(Arrays.asList(ValidateUtils.checkNotNullAndNotEmpty(parsers, "No parsers to aggregate")));
+ }
+
+ static KeyPairResourceParser aggregate(Collection<? extends KeyPairResourceParser> parsers) {
+ ValidateUtils.checkNotNullAndNotEmpty(parsers, "No parsers to aggregate");
+ return new KeyPairResourceParser() {
+ @Override
+ public Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+ throws IOException, GeneralSecurityException {
+ Collection<KeyPair> keyPairs = Collections.emptyList();
+ for (KeyPairResourceParser p : parsers) {
+ if (!p.canExtractKeyPairs(resourceKey, lines)) {
+ continue;
+ }
+
+ Collection<KeyPair> kps = p.loadKeyPairs(resourceKey, passwordProvider, lines);
+ if (GenericUtils.isEmpty(kps)) {
+ continue;
+ }
+
+ if (GenericUtils.isEmpty(keyPairs)) {
+ keyPairs = new LinkedList<>(kps);
+ } else {
+ keyPairs.addAll(kps);
+ }
+ }
+
+ return keyPairs;
+ }
+
+ @Override
+ public boolean canExtractKeyPairs(String resourceKey, List<String> lines) throws IOException, GeneralSecurityException {
+ for (KeyPairResourceParser p : parsers) {
+ if (p.canExtractKeyPairs(resourceKey, lines)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return KeyPairResourceParser.class.getSimpleName() + "[aggregate]";
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyEncryptionContext.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyEncryptionContext.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyEncryptionContext.java
new file mode 100644
index 0000000..5e03cdb
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyEncryptionContext.java
@@ -0,0 +1,270 @@
+/*
+ * 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.loader;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableSet;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+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 class PrivateKeyEncryptionContext implements Cloneable {
+ public static final String DEFAULT_CIPHER_MODE = "CBC";
+
+ private static final Map<String, PrivateKeyObfuscator> OBFUSCATORS =
+ Stream.of(AESPrivateKeyObfuscator.INSTANCE, DESPrivateKeyObfuscator.INSTANCE)
+ .collect(Collectors.toMap(AbstractPrivateKeyObfuscator::getCipherName, Function.identity()));
+
+ private String cipherName;
+ private String cipherType;
+ private String cipherMode = DEFAULT_CIPHER_MODE;
+ private String password;
+ private byte[] initVector;
+ private transient PrivateKeyObfuscator obfuscator;
+
+ public PrivateKeyEncryptionContext() {
+ super();
+ }
+
+ public PrivateKeyEncryptionContext(String algInfo) {
+ parseAlgorithmInfo(algInfo);
+ }
+
+ public String getCipherName() {
+ return cipherName;
+ }
+
+ public void setCipherName(String value) {
+ cipherName = value;
+ }
+
+ public String getCipherType() {
+ return cipherType;
+ }
+
+ public void setCipherType(String value) {
+ cipherType = value;
+ }
+
+ public String getCipherMode() {
+ return cipherMode;
+ }
+
+ public void setCipherMode(String value) {
+ cipherMode = value;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String value) {
+ password = value;
+ }
+
+ public byte[] getInitVector() {
+ return initVector;
+ }
+
+ public void setInitVector(byte... values) {
+ initVector = values;
+ }
+
+ public PrivateKeyObfuscator getPrivateKeyObfuscator() {
+ return obfuscator;
+ }
+
+ public void setPrivateKeyObfuscator(PrivateKeyObfuscator value) {
+ obfuscator = value;
+ }
+
+ public PrivateKeyObfuscator resolvePrivateKeyObfuscator() {
+ PrivateKeyObfuscator value = getPrivateKeyObfuscator();
+ if (value != null) {
+ return value;
+ }
+
+ return getRegisteredPrivateKeyObfuscator(getCipherName());
+ }
+
+ public static PrivateKeyObfuscator registerPrivateKeyObfuscator(PrivateKeyObfuscator o) {
+ return registerPrivateKeyObfuscator(Objects.requireNonNull(o, "No instance provided").getCipherName(), o);
+ }
+
+ public static PrivateKeyObfuscator registerPrivateKeyObfuscator(String cipherName, PrivateKeyObfuscator o) {
+ ValidateUtils.checkNotNullAndNotEmpty(cipherName, "No cipher name");
+ Objects.requireNonNull(o, "No instance provided");
+
+ synchronized (OBFUSCATORS) {
+ return OBFUSCATORS.put(cipherName, o);
+ }
+ }
+
+ public static boolean unregisterPrivateKeyObfuscator(PrivateKeyObfuscator o) {
+ Objects.requireNonNull(o, "No instance provided");
+ String cipherName = o.getCipherName();
+ ValidateUtils.checkNotNullAndNotEmpty(cipherName, "No cipher name");
+
+ synchronized (OBFUSCATORS) {
+ PrivateKeyObfuscator prev = OBFUSCATORS.get(cipherName);
+ if (prev != o) {
+ return false;
+ }
+
+ OBFUSCATORS.remove(cipherName);
+ }
+
+ return true;
+ }
+
+ public static PrivateKeyObfuscator unregisterPrivateKeyObfuscator(String cipherName) {
+ ValidateUtils.checkNotNullAndNotEmpty(cipherName, "No cipher name");
+
+ synchronized (OBFUSCATORS) {
+ return OBFUSCATORS.remove(cipherName);
+ }
+ }
+
+ public static final PrivateKeyObfuscator getRegisteredPrivateKeyObfuscator(String cipherName) {
+ if (GenericUtils.isEmpty(cipherName)) {
+ return null;
+ }
+
+ synchronized (OBFUSCATORS) {
+ return OBFUSCATORS.get(cipherName);
+ }
+ }
+
+ public static final NavigableSet<String> getRegisteredPrivateKeyObfuscatorCiphers() {
+ synchronized (OBFUSCATORS) {
+ Collection<String> names = OBFUSCATORS.keySet();
+ return GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER, names);
+ }
+ }
+
+ public static final List<PrivateKeyObfuscator> getRegisteredPrivateKeyObfuscators() {
+ synchronized (OBFUSCATORS) {
+ Collection<? extends PrivateKeyObfuscator> l = OBFUSCATORS.values();
+ if (GenericUtils.isEmpty(l)) {
+ return Collections.emptyList();
+ } else {
+ return new ArrayList<>(l);
+ }
+ }
+ }
+
+ /**
+ * @param algInfo The algorithm info - format: <I>{@code name-type-mode}</I>
+ * @return The updated context instance
+ * @see #parseAlgorithmInfo(PrivateKeyEncryptionContext, String)
+ */
+ public PrivateKeyEncryptionContext parseAlgorithmInfo(String algInfo) {
+ return parseAlgorithmInfo(this, algInfo);
+ }
+
+ @Override
+ public PrivateKeyEncryptionContext clone() {
+ try {
+ PrivateKeyEncryptionContext copy = getClass().cast(super.clone());
+ byte[] v = copy.getInitVector();
+ if (v != null) {
+ v = v.clone();
+ copy.setInitVector(v);
+ }
+ return copy;
+ } catch (CloneNotSupportedException e) { // unexpected
+ throw new RuntimeException("Failed to clone: " + toString());
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return GenericUtils.hashCode(getCipherName(), Boolean.TRUE)
+ + GenericUtils.hashCode(getCipherType(), Boolean.TRUE)
+ + GenericUtils.hashCode(getCipherMode(), Boolean.TRUE)
+ + Objects.hashCode(getPassword())
+ + Arrays.hashCode(getInitVector());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ PrivateKeyEncryptionContext other = (PrivateKeyEncryptionContext) obj;
+ return (GenericUtils.safeCompare(getCipherName(), other.getCipherName(), false) == 0)
+ && (GenericUtils.safeCompare(getCipherType(), other.getCipherType(), false) == 0)
+ && (GenericUtils.safeCompare(getCipherMode(), other.getCipherMode(), false) == 0)
+ && (GenericUtils.safeCompare(getPassword(), other.getPassword(), true) == 0)
+ && Arrays.equals(getInitVector(), other.getInitVector());
+ }
+
+ @Override
+ public String toString() {
+ return GenericUtils.join(new String[]{getCipherName(), getCipherType(), getCipherMode()}, '-');
+ }
+
+ /**
+ * @param <C> Generic context type
+ * @param context The {@link PrivateKeyEncryptionContext} to update
+ * @param algInfo The algorithm info - format: {@code <I>name</I>-<I>type</I>-<I>mode</I>}
+ * @return The updated context
+ */
+ public static final <C extends PrivateKeyEncryptionContext> C parseAlgorithmInfo(C context, String algInfo) {
+ ValidateUtils.checkNotNullAndNotEmpty(algInfo, "No encryption algorithm data");
+
+ String[] cipherData = GenericUtils.split(algInfo, '-');
+ ValidateUtils.checkTrue(cipherData.length == 3, "Bad encryption algorithm data: %s", algInfo);
+
+ context.setCipherName(cipherData[0]);
+ context.setCipherType(cipherData[1]);
+ context.setCipherMode(cipherData[2]);
+ return context;
+ }
+
+ public static final PrivateKeyEncryptionContext newPrivateKeyEncryptionContext(PrivateKeyObfuscator o, String password) {
+ return initializeObfuscator(new PrivateKeyEncryptionContext(), o, password);
+ }
+
+ public static final <C extends PrivateKeyEncryptionContext> C initializeObfuscator(C context, PrivateKeyObfuscator o, String password) {
+ context.setCipherName(o.getCipherName());
+ context.setPrivateKeyObfuscator(o);
+ context.setPassword(password);
+ return context;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyObfuscator.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyObfuscator.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyObfuscator.java
new file mode 100644
index 0000000..d8d2db5
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/PrivateKeyObfuscator.java
@@ -0,0 +1,64 @@
+/*
+ * 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.loader;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface PrivateKeyObfuscator {
+ /**
+ * @return Basic cipher used to obfuscate
+ */
+ String getCipherName();
+
+ /**
+ * @return A {@link List} of the supported key sizes - <B>Note:</B> every
+ * call returns a and <U>un-modifiable</U> instance.
+ */
+ List<Integer> getSupportedKeySizes();
+
+ /**
+ * @param <A> Appendable generic type
+ * @param sb The {@link Appendable} instance to update
+ * @param encContext
+ * @return Same appendable instance
+ * @throws IOException
+ */
+ <A extends Appendable> A appendPrivateKeyEncryptionContext(A sb, PrivateKeyEncryptionContext encContext) throws IOException;
+
+ /**
+ * @param encContext The encryption context
+ * @return An initialization vector suitable to the specified context
+ * @throws GeneralSecurityException
+ */
+ byte[] generateInitializationVector(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException;
+
+ /**
+ * @param bytes Original bytes
+ * @param encContext The encryption context
+ * @param encryptIt If {@code true} then encrypt the original bytes, otherwise decrypt them
+ * @return The result of applying the cipher to the original bytes
+ * @throws GeneralSecurityException If cannot encrypt/decrypt
+ */
+ byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
new file mode 100644
index 0000000..6188a04
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHDSSPrivateKeyEntryDecoder.java
@@ -0,0 +1,139 @@
+/*
+ * 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.loader.openssh;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class OpenSSHDSSPrivateKeyEntryDecoder extends AbstractPrivateKeyEntryDecoder<DSAPublicKey, DSAPrivateKey> {
+ public static final OpenSSHDSSPrivateKeyEntryDecoder INSTANCE = new OpenSSHDSSPrivateKeyEntryDecoder();
+
+ public OpenSSHDSSPrivateKeyEntryDecoder() {
+ super(DSAPublicKey.class, DSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_DSS)));
+ }
+
+ @Override
+ public DSAPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
+ throws IOException, GeneralSecurityException {
+ if (!KeyPairProvider.SSH_DSS.equals(keyType)) { // just in case we were invoked directly
+ throw new InvalidKeySpecException("Unexpected key type: " + keyType);
+ }
+
+ BigInteger p = KeyEntryResolver.decodeBigInt(keyData);
+ BigInteger q = KeyEntryResolver.decodeBigInt(keyData);
+ BigInteger g = KeyEntryResolver.decodeBigInt(keyData);
+ BigInteger y = KeyEntryResolver.decodeBigInt(keyData);
+ Objects.requireNonNull(y, "No public key data"); // TODO run some validation on it
+ BigInteger x = KeyEntryResolver.decodeBigInt(keyData);
+
+ return generatePrivateKey(new DSAPrivateKeySpec(x, p, q, g));
+ }
+
+ @Override
+ public String encodePrivateKey(OutputStream s, DSAPrivateKey key) throws IOException {
+ Objects.requireNonNull(key, "No private key provided");
+
+ DSAParams keyParams = Objects.requireNonNull(key.getParams(), "No DSA params available");
+ BigInteger p = keyParams.getP();
+ KeyEntryResolver.encodeBigInt(s, p);
+ KeyEntryResolver.encodeBigInt(s, keyParams.getQ());
+
+ BigInteger g = keyParams.getG();
+ KeyEntryResolver.encodeBigInt(s, g);
+
+ BigInteger x = key.getX();
+ BigInteger y = g.modPow(x, p);
+ KeyEntryResolver.encodeBigInt(s, y);
+ KeyEntryResolver.encodeBigInt(s, x);
+ return KeyPairProvider.SSH_DSS;
+ }
+
+ @Override
+ public boolean isPublicKeyRecoverySupported() {
+ return true;
+ }
+
+ @Override
+ public DSAPublicKey recoverPublicKey(DSAPrivateKey privateKey) throws GeneralSecurityException {
+ return KeyUtils.recoverDSAPublicKey(privateKey);
+ }
+
+ @Override
+ public DSAPublicKey clonePublicKey(DSAPublicKey key) throws GeneralSecurityException {
+ if (key == null) {
+ return null;
+ }
+
+ DSAParams params = key.getParams();
+ if (params == null) {
+ throw new InvalidKeyException("Missing parameters in key");
+ }
+
+ return generatePublicKey(new DSAPublicKeySpec(key.getY(), params.getP(), params.getQ(), params.getG()));
+ }
+
+ @Override
+ public DSAPrivateKey clonePrivateKey(DSAPrivateKey key) throws GeneralSecurityException {
+ if (key == null) {
+ return null;
+ }
+
+ DSAParams params = key.getParams();
+ if (params == null) {
+ throw new InvalidKeyException("Missing parameters in key");
+ }
+
+ return generatePrivateKey(new DSAPrivateKeySpec(key.getX(), params.getP(), params.getQ(), params.getG()));
+ }
+
+ @Override
+ public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+ return SecurityUtils.getKeyPairGenerator(KeyUtils.DSS_ALGORITHM);
+ }
+
+ @Override
+ public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+ return SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
new file mode 100644
index 0000000..bceca59
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
@@ -0,0 +1,164 @@
+/*
+ * 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.loader.openssh;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchProviderException;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Objects;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class OpenSSHECDSAPrivateKeyEntryDecoder extends AbstractPrivateKeyEntryDecoder<ECPublicKey, ECPrivateKey> {
+ public static final OpenSSHECDSAPrivateKeyEntryDecoder INSTANCE = new OpenSSHECDSAPrivateKeyEntryDecoder();
+
+ public OpenSSHECDSAPrivateKeyEntryDecoder() {
+ super(ECPublicKey.class, ECPrivateKey.class, ECCurves.KEY_TYPES);
+ }
+
+ @Override
+ public ECPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
+ throws IOException, GeneralSecurityException {
+ ECCurves curve = ECCurves.fromKeyType(keyType);
+ if (curve == null) {
+ throw new InvalidKeySpecException("Not an EC curve name: " + keyType);
+ }
+
+ if (!SecurityUtils.isECCSupported()) {
+ throw new NoSuchProviderException("ECC not supported");
+ }
+
+ String keyCurveName = curve.getName();
+ // see rfc5656 section 3.1
+ String encCurveName = KeyEntryResolver.decodeString(keyData);
+ if (!keyCurveName.equals(encCurveName)) {
+ throw new InvalidKeySpecException("Mismatched key curve name (" + keyCurveName + ") vs. encoded one (" + encCurveName + ")");
+ }
+
+ byte[] pubKey = KeyEntryResolver.readRLEBytes(keyData);
+ Objects.requireNonNull(pubKey, "No public point"); // TODO validate it is a valid ECPoint
+ BigInteger s = KeyEntryResolver.decodeBigInt(keyData);
+ ECParameterSpec params = curve.getParameters();
+ return generatePrivateKey(new ECPrivateKeySpec(s, params));
+ }
+
+ @Override
+ public String encodePrivateKey(OutputStream s, ECPrivateKey key) throws IOException {
+ Objects.requireNonNull(key, "No private key provided");
+ return null;
+ }
+
+ @Override
+ public ECPublicKey recoverPublicKey(ECPrivateKey prvKey) throws GeneralSecurityException {
+ ECCurves curve = ECCurves.fromECKey(prvKey);
+ if (curve == null) {
+ throw new InvalidKeyException("Unknown curve");
+ }
+ // TODO see how we can figure out the public value
+ return super.recoverPublicKey(prvKey);
+ }
+
+ @Override
+ public ECPublicKey clonePublicKey(ECPublicKey key) throws GeneralSecurityException {
+ if (!SecurityUtils.isECCSupported()) {
+ throw new NoSuchProviderException("ECC not supported");
+ }
+
+ if (key == null) {
+ return null;
+ }
+
+ ECParameterSpec params = key.getParams();
+ if (params == null) {
+ throw new InvalidKeyException("Missing parameters in key");
+ }
+
+ return generatePublicKey(new ECPublicKeySpec(key.getW(), params));
+ }
+
+ @Override
+ public ECPrivateKey clonePrivateKey(ECPrivateKey key) throws GeneralSecurityException {
+ if (!SecurityUtils.isECCSupported()) {
+ throw new NoSuchProviderException("ECC not supported");
+ }
+
+ if (key == null) {
+ return null;
+ }
+
+ ECParameterSpec params = key.getParams();
+ if (params == null) {
+ throw new InvalidKeyException("Missing parameters in key");
+ }
+
+ return generatePrivateKey(new ECPrivateKeySpec(key.getS(), params));
+ }
+
+ @Override
+ public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+ if (SecurityUtils.isECCSupported()) {
+ return SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
+ } else {
+ throw new NoSuchProviderException("ECC not supported");
+ }
+ }
+
+ @Override
+ public KeyPair generateKeyPair(int keySize) throws GeneralSecurityException {
+ ECCurves curve = ECCurves.fromCurveSize(keySize);
+ if (curve == null) {
+ throw new InvalidKeySpecException("Unknown curve for key size=" + keySize);
+ }
+
+ KeyPairGenerator gen = getKeyPairGenerator();
+ gen.initialize(curve.getParameters());
+ return gen.generateKeyPair();
+ }
+
+ @Override
+ public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+ if (SecurityUtils.isECCSupported()) {
+ return SecurityUtils.getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
+ } else {
+ throw new NoSuchProviderException("ECC not supported");
+ }
+ }
+}
[37/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java
new file mode 100644
index 0000000..8b4e9ba
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/DirectoryScanner.java
@@ -0,0 +1,380 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.io;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.SelectorUtils;
+
+/**
+ * <p>Class for scanning a directory for files/directories which match certain
+ * criteria.</p>
+ *
+ * <p>These criteria consist of selectors and patterns which have been specified.
+ * With the selectors you can select which files you want to have included.
+ * Files which are not selected are excluded. With patterns you can include
+ * or exclude files based on their filename.</p>
+ *
+ * <p>The idea is simple. A given directory is recursively scanned for all files
+ * and directories. Each file/directory is matched against a set of selectors,
+ * including special support for matching against filenames with include and
+ * and exclude patterns. Only files/directories which match at least one
+ * pattern of the include pattern list or other file selector, and don't match
+ * any pattern of the exclude pattern list or fail to match against a required
+ * selector will be placed in the list of files/directories found.</p>
+ *
+ * <p>When no list of include patterns is supplied, "**" will be used, which
+ * means that everything will be matched. When no list of exclude patterns is
+ * supplied, an empty list is used, such that nothing will be excluded. When
+ * no selectors are supplied, none are applied.</p>
+ *
+ * <p>The filename pattern matching is done as follows:
+ * The name to be matched is split up in path segments. A path segment is the
+ * name of a directory or file, which is bounded by
+ * <code>File.separator</code> ('/' under UNIX, '\' under Windows).
+ * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc",
+ * "def","ghi" and "xyz.java".
+ * The same is done for the pattern against which should be matched.</p>
+ *
+ * <p>The segments of the name and the pattern are then matched against each
+ * other. When '**' is used for a path segment in the pattern, it matches
+ * zero or more path segments of the name.</p>
+ *
+ * <p>There is a special case regarding the use of <code>File.separator</code>s
+ * at the beginning of the pattern and the string to match:<br>
+ * When a pattern starts with a <code>File.separator</code>, the string
+ * to match must also start with a <code>File.separator</code>.
+ * When a pattern does not start with a <code>File.separator</code>, the
+ * string to match may not start with a <code>File.separator</code>.
+ * When one of these rules is not obeyed, the string will not
+ * match.</p>
+ *
+ * <p>When a name path segment is matched against a pattern path segment, the
+ * following special characters can be used:<br>
+ * '*' matches zero or more characters<br>
+ * '?' matches one character.</p>
+ *
+ * <p>Examples:
+ * <br>
+ * <code>"**\*.class"</code> matches all <code>.class</code> files/dirs in a directory tree.
+ * <br>
+ * <code>"test\a??.java"</code> matches all files/dirs which start with an 'a', then two
+ * more characters and then <code>".java"</code>, in a directory called test.
+ * <br>
+ * <code>"**"</code> matches everything in a directory tree.
+ * <br>
+ * <code>"**\test\**\XYZ*"</code> matches all files/dirs which start with <code>"XYZ"</code> and where
+ * there is a parent directory called test (e.g. <code>"abc\test\def\ghi\XYZ123"</code>).
+ * </p>
+ *
+ * <p>Case sensitivity may be turned off if necessary. By default, it is
+ * turned on.</p>
+ *
+ * <p>Example of usage:</p>
+ * <pre>
+ * String[] includes = {"**\\*.class"};
+ * String[] excludes = {"modules\\*\\**"};
+ * ds.setIncludes(includes);
+ * ds.setExcludes(excludes);
+ * ds.setBasedir(new File("test"));
+ * ds.setCaseSensitive(true);
+ * ds.scan();
+ *
+ * System.out.println("FILES:");
+ * String[] files = ds.getIncludedFiles();
+ * for (int i = 0; i < files.length; i++) {
+ * System.out.println(files[i]);
+ * }
+ * </pre>
+ * <p>This will scan a directory called test for .class files, but excludes all
+ * files in all proper subdirectories of a directory called "modules".</p>
+ *
+ * @author Arnout J. Kuiper
+ * <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a>
+ * @author Magesh Umasankar
+ * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a>
+ * @author <a href="mailto:levylambert@tiscali-dsl.de">Antoine Levy-Lambert</a>
+ */
+public class DirectoryScanner {
+
+ /**
+ * The base directory to be scanned.
+ */
+ protected File basedir;
+
+ /**
+ * The patterns for the files to be included.
+ */
+ protected String[] includes;
+
+ /**
+ * The files which matched at least one include and no excludes
+ * and were selected.
+ */
+ protected List<String> filesIncluded;
+
+ /**
+ * Whether or not the file system should be treated as a case sensitive
+ * one.
+ */
+ protected boolean isCaseSensitive = true;
+
+ public DirectoryScanner() {
+ super();
+ }
+
+ public DirectoryScanner(String basedir, String... includes) {
+ setBasedir(basedir);
+ setIncludes(includes);
+ }
+
+ /**
+ * Sets the base directory to be scanned. This is the directory which is
+ * scanned recursively. All '/' and '\' characters are replaced by
+ * <code>File.separatorChar</code>, so the separator used need not match
+ * <code>File.separatorChar</code>.
+ *
+ * @param basedir The base directory to scan.
+ * Must not be {@code null}.
+ */
+ public void setBasedir(String basedir) {
+ setBasedir(new File(basedir.replace('/', File.separatorChar).replace('\\', File.separatorChar)));
+ }
+
+ /**
+ * Sets the base directory to be scanned. This is the directory which is
+ * scanned recursively.
+ *
+ * @param basedir The base directory for scanning.
+ * Should not be {@code null}.
+ */
+ public void setBasedir(File basedir) {
+ this.basedir = basedir;
+ }
+
+ /**
+ * Returns the base directory to be scanned.
+ * This is the directory which is scanned recursively.
+ *
+ * @return the base directory to be scanned
+ */
+ public File getBasedir() {
+ return basedir;
+ }
+
+ /**
+ * <p>Sets the list of include patterns to use. All '/' and '\' characters
+ * are replaced by <code>File.separatorChar</code>, so the separator used
+ * need not match <code>File.separatorChar</code>.</p>
+ *
+ * <p>When a pattern ends with a '/' or '\', "**" is appended.</p>
+ *
+ * @param includes A list of include patterns.
+ * May be {@code null}, indicating that all files
+ * should be included. If a non-{@code null}
+ * list is given, all elements must be
+ * non-{@code null}.
+ */
+ public void setIncludes(String[] includes) {
+ if (includes == null) {
+ this.includes = null;
+ } else {
+ this.includes = new String[includes.length];
+ for (int i = 0; i < includes.length; i++) {
+ this.includes[i] = normalizePattern(includes[i]);
+ }
+ }
+ }
+
+ /**
+ * Scans the base directory for files which match at least one include
+ * pattern and don't match any exclude patterns. If there are selectors
+ * then the files must pass muster there, as well.
+ *
+ * @return the matching files
+ * @throws IllegalStateException if the base directory was set
+ * incorrectly (i.e. if it is {@code null}, doesn't exist,
+ * or isn't a directory).
+ */
+ public String[] scan() throws IllegalStateException {
+ if (basedir == null) {
+ throw new IllegalStateException("No basedir set");
+ }
+ if (!basedir.exists()) {
+ throw new IllegalStateException("basedir " + basedir
+ + " does not exist");
+ }
+ if (!basedir.isDirectory()) {
+ throw new IllegalStateException("basedir " + basedir
+ + " is not a directory");
+ }
+ if (includes == null || includes.length == 0) {
+ throw new IllegalStateException("No includes set ");
+ }
+
+ filesIncluded = new ArrayList<>();
+
+ scandir(basedir, "");
+
+ return getIncludedFiles();
+ }
+
+ /**
+ * Scans the given directory for files and directories. Found files and
+ * directories are placed in their respective collections, based on the
+ * matching of includes, excludes, and the selectors. When a directory
+ * is found, it is scanned recursively.
+ *
+ * @param dir The directory to scan. Must not be {@code null}.
+ * @param vpath The path relative to the base directory (needed to
+ * prevent problems with an absolute path when using
+ * dir). Must not be {@code null}.
+ */
+ protected void scandir(File dir, String vpath) {
+ String[] newfiles = dir.list();
+ if (GenericUtils.isEmpty(newfiles)) {
+ newfiles = GenericUtils.EMPTY_STRING_ARRAY;
+ }
+
+ for (String newfile : newfiles) {
+ String name = vpath + newfile;
+ File file = new File(dir, newfile);
+ if (file.isDirectory()) {
+ if (isIncluded(name)) {
+ filesIncluded.add(name);
+ scandir(file, name + File.separator);
+ } else if (couldHoldIncluded(name)) {
+ scandir(file, name + File.separator);
+ }
+ } else if (file.isFile()) {
+ if (isIncluded(name)) {
+ filesIncluded.add(name);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the names of the files which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ * The names are relative to the base directory.
+ *
+ * @return the names of the files which matched at least one of the
+ * include patterns and none of the exclude patterns.
+ */
+ public String[] getIncludedFiles() {
+ String[] files = new String[filesIncluded.size()];
+ return filesIncluded.toArray(files);
+ }
+
+ /**
+ * Tests whether or not a name matches against at least one include
+ * pattern.
+ *
+ * @param name The name to match. Must not be {@code null}.
+ * @return <code>true</code> when the name matches against at least one
+ * include pattern, or <code>false</code> otherwise.
+ */
+ protected boolean isIncluded(String name) {
+ for (String include : includes) {
+ if (SelectorUtils.matchPath(include, name, isCaseSensitive)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tests whether or not a name matches the start of at least one include
+ * pattern.
+ *
+ * @param name The name to match. Must not be {@code null}.
+ * @return <code>true</code> when the name matches against the start of at
+ * least one include pattern, or <code>false</code> otherwise.
+ */
+ protected boolean couldHoldIncluded(String name) {
+ for (String include : includes) {
+ if (SelectorUtils.matchPatternStart(include, name, isCaseSensitive)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Normalizes the pattern, e.g. converts forward and backward slashes to the platform-specific file separator.
+ *
+ * @param pattern The pattern to normalize, must not be {@code null}.
+ * @return The normalized pattern, never {@code null}.
+ */
+ private String normalizePattern(String pattern) {
+ pattern = pattern.trim();
+
+ if (pattern.startsWith(SelectorUtils.REGEX_HANDLER_PREFIX)) {
+ if (File.separatorChar == '\\') {
+ pattern = replace(pattern, "/", "\\\\", -1);
+ } else {
+ pattern = replace(pattern, "\\\\", "/", -1);
+ }
+ } else {
+ pattern = pattern.replace(File.separatorChar == '/' ? '\\' : '/', File.separatorChar);
+
+ if (pattern.endsWith(File.separator)) {
+ pattern += "**";
+ }
+ }
+
+ return pattern;
+ }
+
+ /**
+ * <p>Replace a String with another String inside a larger String,
+ * for the first <code>max</code> values of the search String.</p>
+ *
+ * <p>A {@code null} reference passed to this method is a no-op.</p>
+ *
+ * @param text text to search and replace in
+ * @param repl String to search for
+ * @param with String to replace with
+ * @param max maximum number of values to replace, or <code>-1</code> if no maximum
+ * @return the text with any replacements processed
+ */
+ @SuppressWarnings("PMD.AssignmentInOperand")
+ public static String replace(String text, String repl, String with, int max) {
+ if ((text == null) || (repl == null) || (with == null) || (repl.length() == 0)) {
+ return text;
+ }
+
+ StringBuilder buf = new StringBuilder(text.length());
+ int start = 0;
+ for (int end = text.indexOf(repl, start); end != -1; end = text.indexOf(repl, start)) {
+ buf.append(text.substring(start, end)).append(with);
+ start = end + repl.length();
+
+ if (--max == 0) {
+ break;
+ }
+ }
+ buf.append(text.substring(start));
+ return buf.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java
new file mode 100644
index 0000000..8a1a68d
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/EmptyInputStream.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A {@code /dev/null} implementation - always open
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class EmptyInputStream extends InputStream {
+ public static final EmptyInputStream DEV_NULL = new EmptyInputStream();
+
+ public EmptyInputStream() {
+ super();
+ }
+
+ @Override
+ public int read() throws IOException {
+ return -1;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ return -1;
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ return 0L;
+ }
+
+ @Override
+ public int available() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public synchronized void mark(int readlimit) {
+ throw new UnsupportedOperationException("mark(" + readlimit + ") called despite the fact that markSupported=" + markSupported());
+ }
+
+ @Override
+ public synchronized void reset() throws IOException {
+ // ignored
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java
new file mode 100644
index 0000000..feafd18
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/FileInfoExtractor.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.Set;
+
+/**
+ * @param <T> Type of information being extracted
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface FileInfoExtractor<T> {
+
+ FileInfoExtractor<Boolean> EXISTS = Files::exists;
+
+ FileInfoExtractor<Boolean> ISDIR = Files::isDirectory;
+
+ FileInfoExtractor<Boolean> ISREG = Files::isRegularFile;
+
+ FileInfoExtractor<Boolean> ISSYMLINK = (file, options) -> Files.isSymbolicLink(file);
+
+ FileInfoExtractor<Long> SIZE = (file, options) -> Files.size(file);
+
+ FileInfoExtractor<Set<PosixFilePermission>> PERMISSIONS = IoUtils::getPermissions;
+
+ FileInfoExtractor<FileTime> LASTMODIFIED = Files::getLastModifiedTime;
+
+ T infoOf(Path file, LinkOption... options) throws IOException;
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.java
new file mode 100644
index 0000000..d847079
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/InputStreamWithChannel.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.util.io;
+
+import java.io.InputStream;
+import java.nio.channels.Channel;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class InputStreamWithChannel extends InputStream implements Channel {
+ protected InputStreamWithChannel() {
+ super();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/IoUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/IoUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/IoUtils.java
new file mode 100644
index 0000000..10aa59a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/IoUtils.java
@@ -0,0 +1,556 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.io;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.CopyOption;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.UserPrincipal;
+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.Objects;
+import java.util.Set;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.OsUtils;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class IoUtils {
+
+ public static final OpenOption[] EMPTY_OPEN_OPTIONS = new OpenOption[0];
+ public static final CopyOption[] EMPTY_COPY_OPTIONS = new CopyOption[0];
+ public static final LinkOption[] EMPTY_LINK_OPTIONS = new LinkOption[0];
+ public static final FileAttribute<?>[] EMPTY_FILE_ATTRIBUTES = new FileAttribute<?>[0];
+
+ public static final List<String> WINDOWS_EXECUTABLE_EXTENSIONS = Collections.unmodifiableList(Arrays.asList(".bat", ".exe", ".cmd"));
+
+ /**
+ * Size of preferred work buffer when reading / writing data to / from streams
+ */
+ public static final int DEFAULT_COPY_SIZE = 8192;
+
+ /**
+ * The local O/S line separator
+ */
+ public static final String EOL = System.lineSeparator();
+
+ /**
+ * A {@link Set} of {@link StandardOpenOption}-s that indicate an intent
+ * to create/modify a file
+ */
+ public static final Set<StandardOpenOption> WRITEABLE_OPEN_OPTIONS =
+ Collections.unmodifiableSet(
+ EnumSet.of(
+ StandardOpenOption.APPEND, StandardOpenOption.CREATE,
+ StandardOpenOption.CREATE_NEW, StandardOpenOption.DELETE_ON_CLOSE,
+ StandardOpenOption.DSYNC, StandardOpenOption.SYNC,
+ StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE));
+
+ private static final byte[] EOL_BYTES = EOL.getBytes(StandardCharsets.UTF_8);
+
+ private static final LinkOption[] NO_FOLLOW_OPTIONS = new LinkOption[]{LinkOption.NOFOLLOW_LINKS};
+
+ /**
+ * Private Constructor
+ */
+ private IoUtils() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+
+ /**
+ * @return The local platform line separator bytes as UTF-8. <B>Note:</B>
+ * each call returns a <U>new</U> instance in order to avoid inadvertent
+ * changes in shared objects
+ * @see #EOL
+ */
+ public static byte[] getEOLBytes() {
+ return EOL_BYTES.clone();
+ }
+
+ public static LinkOption[] getLinkOptions(boolean followLinks) {
+ if (followLinks) {
+ return EMPTY_LINK_OPTIONS;
+ } else { // return a clone that modifications to the array will not affect others
+ return NO_FOLLOW_OPTIONS.clone();
+ }
+ }
+
+ public static long copy(InputStream source, OutputStream sink) throws IOException {
+ return copy(source, sink, DEFAULT_COPY_SIZE);
+ }
+
+ public static long copy(InputStream source, OutputStream sink, int bufferSize) throws IOException {
+ long nread = 0L;
+ byte[] buf = new byte[bufferSize];
+ for (int n = source.read(buf); n > 0; n = source.read(buf)) {
+ sink.write(buf, 0, n);
+ nread += n;
+ }
+
+ return nread;
+ }
+
+ /**
+ * Closes a bunch of resources suppressing any {@link IOException}s their
+ * {@link Closeable#close()} method may have thrown
+ *
+ * @param closeables The {@link Closeable}s to close
+ * @return The <U>first</U> {@link IOException} that occurred during closing
+ * of a resource - if more than one exception occurred, they are added as
+ * suppressed exceptions to the first one
+ * @see Throwable#getSuppressed()
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public static IOException closeQuietly(Closeable... closeables) {
+ IOException err = null;
+ for (Closeable c : closeables) {
+ try {
+ if (c != null) {
+ c.close();
+ }
+ } catch (IOException e) {
+ err = GenericUtils.accumulateException(err, e);
+ }
+ }
+
+ return err;
+ }
+
+ /**
+ * @param fileName The file name to be evaluated - ignored if {@code null}/empty
+ * @return {@code true} if the file ends in one of the {@link #WINDOWS_EXECUTABLE_EXTENSIONS}
+ */
+ public static boolean isWindowsExecutable(String fileName) {
+ if ((fileName == null) || (fileName.length() <= 0)) {
+ return false;
+ }
+ for (String suffix : WINDOWS_EXECUTABLE_EXTENSIONS) {
+ if (fileName.endsWith(suffix)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * If the "posix" view is supported, then it returns
+ * {@link Files#getPosixFilePermissions(Path, LinkOption...)}, otherwise
+ * uses the {@link #getPermissionsFromFile(File)} method
+ *
+ * @param path The {@link Path}
+ * @param options The {@link LinkOption}s to use when querying the permissions
+ * @return A {@link Set} of {@link PosixFilePermission}
+ * @throws IOException If failed to access the file system in order to
+ * retrieve the permissions
+ */
+ public static Set<PosixFilePermission> getPermissions(Path path, LinkOption... options) throws IOException {
+ FileSystem fs = path.getFileSystem();
+ Collection<String> views = fs.supportedFileAttributeViews();
+ if (views.contains("posix")) {
+ return Files.getPosixFilePermissions(path, options);
+ } else {
+ return getPermissionsFromFile(path.toFile());
+ }
+ }
+
+ /**
+ * @param f The {@link File} to be checked
+ * @return A {@link Set} of {@link PosixFilePermission}s based on whether
+ * the file is readable/writable/executable. If so, then <U>all</U> the
+ * relevant permissions are set (i.e., owner, group and others)
+ */
+ public static Set<PosixFilePermission> getPermissionsFromFile(File f) {
+ Set<PosixFilePermission> perms = EnumSet.noneOf(PosixFilePermission.class);
+ if (f.canRead()) {
+ perms.add(PosixFilePermission.OWNER_READ);
+ perms.add(PosixFilePermission.GROUP_READ);
+ perms.add(PosixFilePermission.OTHERS_READ);
+ }
+
+ if (f.canWrite()) {
+ perms.add(PosixFilePermission.OWNER_WRITE);
+ perms.add(PosixFilePermission.GROUP_WRITE);
+ perms.add(PosixFilePermission.OTHERS_WRITE);
+ }
+
+ if (isExecutable(f)) {
+ perms.add(PosixFilePermission.OWNER_EXECUTE);
+ perms.add(PosixFilePermission.GROUP_EXECUTE);
+ perms.add(PosixFilePermission.OTHERS_EXECUTE);
+ }
+
+ return perms;
+ }
+
+ public static boolean isExecutable(File f) {
+ if (f == null) {
+ return false;
+ }
+
+ if (OsUtils.isWin32()) {
+ return isWindowsExecutable(f.getName());
+ } else {
+ return f.canExecute();
+ }
+ }
+
+ /**
+ * If the "posix" view is supported, then it invokes
+ * {@link Files#setPosixFilePermissions(Path, Set)}, otherwise
+ * uses the {@link #setPermissionsToFile(File, Collection)} method
+ *
+ * @param path The {@link Path}
+ * @param perms The {@link Set} of {@link PosixFilePermission}s
+ * @throws IOException If failed to access the file system
+ */
+ public static void setPermissions(Path path, Set<PosixFilePermission> perms) throws IOException {
+ FileSystem fs = path.getFileSystem();
+ Collection<String> views = fs.supportedFileAttributeViews();
+ if (views.contains("posix")) {
+ Files.setPosixFilePermissions(path, perms);
+ } else {
+ setPermissionsToFile(path.toFile(), perms);
+ }
+ }
+
+ /**
+ * @param f The {@link File}
+ * @param perms A {@link Collection} of {@link PosixFilePermission}s to set on it.
+ * <B>Note:</B> the file is set to readable/writable/executable not only by the
+ * owner if <U>any</U> of relevant the owner/group/others permission is set
+ */
+ public static void setPermissionsToFile(File f, Collection<PosixFilePermission> perms) {
+ boolean readable = perms != null
+ && (perms.contains(PosixFilePermission.OWNER_READ)
+ || perms.contains(PosixFilePermission.GROUP_READ)
+ || perms.contains(PosixFilePermission.OTHERS_READ));
+ f.setReadable(readable, false);
+
+ boolean writable = perms != null
+ && (perms.contains(PosixFilePermission.OWNER_WRITE)
+ || perms.contains(PosixFilePermission.GROUP_WRITE)
+ || perms.contains(PosixFilePermission.OTHERS_WRITE));
+ f.setWritable(writable, false);
+
+ boolean executable = perms != null
+ && (perms.contains(PosixFilePermission.OWNER_EXECUTE)
+ || perms.contains(PosixFilePermission.GROUP_EXECUTE)
+ || perms.contains(PosixFilePermission.OTHERS_EXECUTE));
+ f.setExecutable(executable, false);
+ }
+
+ /**
+ * <P>Get file owner.</P>
+ *
+ * @param path The {@link Path}
+ * @param options The {@link LinkOption}s to use when querying the owner
+ * @return Owner of the file or null if unsupported. <B>Note:</B> for
+ * <I>Windows</I> it strips any prepended domain or group name
+ * @throws IOException If failed to access the file system
+ * @see Files#getOwner(Path, LinkOption...)
+ */
+ public static String getFileOwner(Path path, LinkOption... options) throws IOException {
+ try {
+ UserPrincipal principal = Files.getOwner(path, options);
+ String owner = (principal == null) ? null : principal.getName();
+ return OsUtils.getCanonicalUser(owner);
+ } catch (UnsupportedOperationException e) {
+ return null;
+ }
+ }
+
+ /**
+ * <P>Checks if a file exists - <B>Note:</B> according to the
+ * <A HREF="http://docs.oracle.com/javase/tutorial/essential/io/check.html">Java tutorial - Checking a File or Directory</A>:
+ * </P>
+ *
+ * <PRE>
+ * The methods in the Path class are syntactic, meaning that they operate
+ * on the Path instance. But eventually you must access the file system
+ * to verify that a particular Path exists, or does not exist. You can do
+ * so with the exists(Path, LinkOption...) and the notExists(Path, LinkOption...)
+ * methods. Note that !Files.exists(path) is not equivalent to Files.notExists(path).
+ * When you are testing a file's existence, three results are possible:
+ *
+ * - The file is verified to exist.
+ * - The file is verified to not exist.
+ * - The file's status is unknown.
+ *
+ * This result can occur when the program does not have access to the file.
+ * If both exists and notExists return false, the existence of the file cannot
+ * be verified.
+ * </PRE>
+ *
+ * @param path The {@link Path} to be tested
+ * @param options The {@link LinkOption}s to use
+ * @return {@link Boolean#TRUE}/{@link Boolean#FALSE} or {@code null}
+ * according to the file status as explained above
+ */
+ public static Boolean checkFileExists(Path path, LinkOption... options) {
+ if (Files.exists(path, options)) {
+ return Boolean.TRUE;
+ } else if (Files.notExists(path, options)) {
+ return Boolean.FALSE;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Read the requested number of bytes or fail if there are not enough left.
+ *
+ * @param input where to read input from
+ * @param buffer destination
+ * @throws IOException if there is a problem reading the file
+ * @throws EOFException if the number of bytes read was incorrect
+ */
+ public static void readFully(InputStream input, byte[] buffer) throws IOException {
+ readFully(input, buffer, 0, buffer.length);
+ }
+
+ /**
+ * Read the requested number of bytes or fail if there are not enough left.
+ *
+ * @param input where to read input from
+ * @param buffer destination
+ * @param offset initial offset into buffer
+ * @param length length to read, must be ≥ 0
+ * @throws IOException if there is a problem reading the file
+ * @throws EOFException if the number of bytes read was incorrect
+ */
+ public static void readFully(InputStream input, byte[] buffer, int offset, int length) throws IOException {
+ int actual = read(input, buffer, offset, length);
+ if (actual != length) {
+ throw new EOFException("Premature EOF - expected=" + length + ", actual=" + actual);
+ }
+ }
+
+ /**
+ * Read as many bytes as possible until EOF or achieved required length
+ *
+ * @param input where to read input from
+ * @param buffer destination
+ * @return actual length read; may be less than requested if EOF was reached
+ * @throws IOException if a read error occurs
+ */
+ public static int read(InputStream input, byte[] buffer) throws IOException {
+ return read(input, buffer, 0, buffer.length);
+ }
+
+ /**
+ * Read as many bytes as possible until EOF or achieved required length
+ *
+ * @param input where to read input from
+ * @param buffer destination
+ * @param offset initial offset into buffer
+ * @param length length to read - ignored if non-positive
+ * @return actual length read; may be less than requested if EOF was reached
+ * @throws IOException if a read error occurs
+ */
+ public static int read(InputStream input, byte[] buffer, int offset, int length) throws IOException {
+ for (int remaining = length, curOffset = offset; remaining > 0;) {
+ int count = input.read(buffer, curOffset, remaining);
+ if (count == -1) { // EOF before achieved required length
+ return curOffset - offset;
+ }
+
+ remaining -= count;
+ curOffset += count;
+ }
+
+ return length;
+ }
+
+ /**
+ * @param perms The current {@link PosixFilePermission}s - ignored if {@code null}/empty
+ * @param excluded The permissions <U>not</U> allowed to exist - ignored if {@code null}/empty
+ * @return The violating {@link PosixFilePermission} - {@code null}
+ * if no violating permission found
+ */
+ public static PosixFilePermission validateExcludedPermissions(Collection<PosixFilePermission> perms, Collection<PosixFilePermission> excluded) {
+ if (GenericUtils.isEmpty(perms) || GenericUtils.isEmpty(excluded)) {
+ return null;
+ }
+
+ for (PosixFilePermission p : excluded) {
+ if (perms.contains(p)) {
+ return p;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param path The {@link Path} to check
+ * @param options The {@link LinkOption}s to use when checking if path is a directory
+ * @return The same input path if it is a directory
+ * @throws UnsupportedOperationException if input path not a directory
+ */
+ public static Path ensureDirectory(Path path, LinkOption... options) {
+ if (!Files.isDirectory(path, options)) {
+ throw new UnsupportedOperationException("Not a directory: " + path);
+ }
+ return path;
+ }
+
+ /**
+ * @param options The {@link LinkOption}s - OK if {@code null}/empty
+ * @return {@code true} if the link options are {@code null}/empty or do
+ * not contain {@link LinkOption#NOFOLLOW_LINKS}, {@code false} otherwise
+ * (i.e., the array is not empty and contains the special value)
+ */
+ public static boolean followLinks(LinkOption... options) {
+ if (GenericUtils.isEmpty(options)) {
+ return true;
+ }
+
+ for (LinkOption localLinkOption : options) {
+ if (localLinkOption == LinkOption.NOFOLLOW_LINKS) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static String appendPathComponent(String prefix, String component) {
+ if (GenericUtils.isEmpty(prefix)) {
+ return component;
+ }
+
+ if (GenericUtils.isEmpty(component)) {
+ return prefix;
+ }
+
+ StringBuilder sb = new StringBuilder(prefix.length() + component.length() + File.separator.length()).append(prefix);
+
+ if (sb.charAt(prefix.length() - 1) == File.separatorChar) {
+ if (component.charAt(0) == File.separatorChar) {
+ sb.append(component.substring(1));
+ } else {
+ sb.append(component);
+ }
+ } else {
+ if (component.charAt(0) != File.separatorChar) {
+ sb.append(File.separatorChar);
+ }
+ sb.append(component);
+ }
+
+ return sb.toString();
+ }
+
+ public static byte[] toByteArray(InputStream inStream) throws IOException {
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream(DEFAULT_COPY_SIZE)) {
+ copy(inStream, baos);
+ return baos.toByteArray();
+ }
+ }
+
+ /**
+ * Reads all lines until no more available
+ *
+ * @param url The {@link URL} to read from
+ * @return The {@link List} of lines in the same <U>order</U> as it was read
+ * @throws IOException If failed to read the lines
+ * @see #readAllLines(InputStream)
+ */
+ public static List<String> readAllLines(URL url) throws IOException {
+ try (InputStream stream = Objects.requireNonNull(url, "No URL").openStream()) {
+ return readAllLines(stream);
+ }
+ }
+
+ /**
+ * Reads all lines until no more available
+ *
+ * @param stream The {@link InputStream} - <B>Note:</B> assumed to
+ * contain {@code UTF-8} encoded data
+ * @return The {@link List} of lines in the same <U>order</U> as it was read
+ * @throws IOException If failed to read the lines
+ * @see #readAllLines(Reader)
+ */
+ public static List<String> readAllLines(InputStream stream) throws IOException {
+ try (Reader reader = new InputStreamReader(Objects.requireNonNull(stream, "No stream instance"), StandardCharsets.UTF_8)) {
+ return readAllLines(reader);
+ }
+ }
+
+ public static List<String> readAllLines(Reader reader) throws IOException {
+ try (BufferedReader br = new BufferedReader(Objects.requireNonNull(reader, "No reader instance"), DEFAULT_COPY_SIZE)) {
+ return readAllLines(br);
+ }
+ }
+
+ /**
+ * Reads all lines until no more available
+ *
+ * @param reader The {@link BufferedReader} to read all lines
+ * @return The {@link List} of lines in the same <U>order</U> as it was read
+ * @throws IOException If failed to read the lines
+ * @see #readAllLines(BufferedReader, int)
+ */
+ public static List<String> readAllLines(BufferedReader reader) throws IOException {
+ return readAllLines(reader, -1);
+ }
+
+ /**
+ * Reads all lines until no more available
+ *
+ * @param reader The {@link BufferedReader} to read all lines
+ * @param lineCountHint A hint as to the expected number of lines - non-positive
+ * means unknown - in which case some initial default value will be used to
+ * initialize the list used to accumulate the lines.
+ * @return The {@link List} of lines in the same <U>order</U> as it was read
+ * @throws IOException If failed to read the lines
+ */
+ public static List<String> readAllLines(BufferedReader reader, int lineCountHint) throws IOException {
+ List<String> result = new ArrayList<>(Math.max(lineCountHint, Short.SIZE));
+ for (String line = reader.readLine(); line != null; line = reader.readLine()) {
+ result.add(line);
+ }
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.java
new file mode 100644
index 0000000..bbf956a
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/LimitInputStream.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.util.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.Channel;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Reads from another {@link InputStream} up to specified max. length
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class LimitInputStream extends FilterInputStream implements Channel {
+ private final AtomicBoolean open = new AtomicBoolean(true);
+ private long remaining;
+
+ public LimitInputStream(InputStream in, long length) {
+ super(in);
+ remaining = length;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return open.get();
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (!isOpen()) {
+ throw new IOException("read() - stream is closed (remaining=" + remaining + ")");
+ }
+
+ if (remaining > 0) {
+ remaining--;
+ return super.read();
+ } else {
+ return -1;
+ }
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (!isOpen()) {
+ throw new IOException("read(len=" + len + ") stream is closed (remaining=" + remaining + ")");
+ }
+
+ int nb = len;
+ if (nb > remaining) {
+ nb = (int) remaining;
+ }
+ if (nb > 0) {
+ int read = super.read(b, off, nb);
+ remaining -= read;
+ return read;
+ } else {
+ return -1;
+ }
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ if (!isOpen()) {
+ throw new IOException("skip(" + n + ") stream is closed (remaining=" + remaining + ")");
+ }
+
+ long skipped = super.skip(n);
+ remaining -= skipped;
+ return skipped;
+ }
+
+ @Override
+ public int available() throws IOException {
+ if (!isOpen()) {
+ throw new IOException("available() stream is closed (remaining=" + remaining + ")");
+ }
+
+ int av = super.available();
+ if (av > remaining) {
+ return (int) remaining;
+ } else {
+ return av;
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ // do not close the original input stream since it serves for ACK(s)
+ if (open.getAndSet(false)) {
+ //noinspection UnnecessaryReturnStatement
+ return; // debug breakpoint
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
new file mode 100644
index 0000000..ff4dbb6
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/LoggingFilterOutputStream.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.io;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.common.PropertyResolver;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.logging.LoggingUtils;
+import org.apache.sshd.common.util.logging.SimplifiedLog;
+import org.slf4j.Logger;
+
+/**
+ * Dumps everything that is written to the stream to the logger
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class LoggingFilterOutputStream extends FilterOutputStream {
+
+ private final String msg;
+ private final SimplifiedLog log;
+ private final int chunkSize;
+ private final AtomicInteger writeCount = new AtomicInteger(0);
+
+ public LoggingFilterOutputStream(OutputStream out, String msg, Logger log, PropertyResolver resolver) {
+ this(out, msg, log, resolver.getIntProperty(BufferUtils.HEXDUMP_CHUNK_SIZE, BufferUtils.DEFAULT_HEXDUMP_CHUNK_SIZE));
+ }
+
+ public LoggingFilterOutputStream(OutputStream out, String msg, Logger log, int chunkSize) {
+ super(out);
+ this.msg = msg;
+ this.log = LoggingUtils.wrap(log);
+ this.chunkSize = chunkSize;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ byte[] d = new byte[1];
+ d[0] = (byte) b;
+ write(d, 0, 1);
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ int count = writeCount.incrementAndGet();
+ BufferUtils.dumpHex(log, BufferUtils.DEFAULT_HEXDUMP_LEVEL, msg + "[" + count + "]", BufferUtils.DEFAULT_HEX_SEPARATOR, chunkSize, b, off, len);
+ out.write(b, off, len);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java
new file mode 100644
index 0000000..032260b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/ModifiableFileWatcher.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.OsUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * Watches over changes for a file and re-loads them if file has changed - including
+ * if file is deleted or (re-)created
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ModifiableFileWatcher extends AbstractLoggingBean {
+
+ /**
+ * The {@link Set} of {@link PosixFilePermission} <U>not</U> allowed if strict
+ * permissions are enforced on key files
+ */
+ public static final Set<PosixFilePermission> STRICTLY_PROHIBITED_FILE_PERMISSION =
+ Collections.unmodifiableSet(
+ EnumSet.of(PosixFilePermission.GROUP_WRITE, PosixFilePermission.OTHERS_WRITE));
+
+ protected final LinkOption[] options;
+
+ private final Path file;
+ private final AtomicBoolean lastExisted = new AtomicBoolean(false);
+ private final AtomicLong lastSize = new AtomicLong(Long.MIN_VALUE);
+ private final AtomicLong lastModified = new AtomicLong(-1L);
+
+ public ModifiableFileWatcher(File file) {
+ this(Objects.requireNonNull(file, "No file to watch").toPath());
+ }
+
+ public ModifiableFileWatcher(Path file) {
+ this(file, IoUtils.getLinkOptions(true));
+ }
+
+ public ModifiableFileWatcher(Path file, LinkOption... options) {
+ this.file = Objects.requireNonNull(file, "No path to watch");
+ // use a clone to avoid being sensitive to changes in the passed array
+ this.options = (options == null) ? IoUtils.EMPTY_LINK_OPTIONS : options.clone();
+ }
+
+ /**
+ * @return The watched {@link Path}
+ */
+ public final Path getPath() {
+ return file;
+ }
+
+ public final boolean exists() throws IOException {
+ return Files.exists(getPath(), options);
+ }
+
+ public final long size() throws IOException {
+ if (exists()) {
+ return Files.size(getPath());
+ } else {
+ return -1L;
+ }
+ }
+
+ public final FileTime lastModified() throws IOException {
+ if (exists()) {
+ BasicFileAttributes attrs = Files.readAttributes(getPath(), BasicFileAttributes.class, options);
+ return attrs.lastModifiedTime();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @return {@code true} if the watched file has probably been changed
+ * @throws IOException If failed to query file data
+ */
+ public boolean checkReloadRequired() throws IOException {
+ boolean exists = exists();
+ // if existence state changed from last time
+ if (exists != lastExisted.getAndSet(exists)) {
+ return true;
+ }
+
+ if (!exists) {
+ // file did not exist and still does not exist
+ resetReloadAttributes();
+ return false;
+ }
+
+ long size = size();
+ if (size < 0L) {
+ // means file no longer exists
+ resetReloadAttributes();
+ return true;
+ }
+
+ // if size changed then obviously need reload
+ if (size != lastSize.getAndSet(size)) {
+ return true;
+ }
+
+ FileTime modifiedTime = lastModified();
+ if (modifiedTime == null) {
+ // means file no longer exists
+ resetReloadAttributes();
+ return true;
+ }
+
+ long timestamp = modifiedTime.toMillis();
+ return timestamp != lastModified.getAndSet(timestamp);
+
+ }
+
+ /**
+ * Resets the state attributes used to detect changes to the initial
+ * construction values - i.e., file assumed not to exist and no known
+ * size of modify time
+ */
+ public void resetReloadAttributes() {
+ lastExisted.set(false);
+ lastSize.set(Long.MIN_VALUE);
+ lastModified.set(-1L);
+ }
+
+ /**
+ * May be called to refresh the state attributes used to detect changes
+ * e.g., file existence, size and last-modified time once re-loading is
+ * successfully completed. If the file does not exist then the attributes
+ * are reset to an "unknown" state.
+ *
+ * @throws IOException If failed to access the file (if exists)
+ * @see #resetReloadAttributes()
+ */
+ public void updateReloadAttributes() throws IOException {
+ if (exists()) {
+ long size = size();
+ FileTime modifiedTime = lastModified();
+
+ if ((size >= 0L) && (modifiedTime != null)) {
+ lastExisted.set(true);
+ lastSize.set(size);
+ lastModified.set(modifiedTime.toMillis());
+ return;
+ }
+ }
+
+ resetReloadAttributes();
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toString(getPath());
+ }
+
+ /**
+ * <P>Checks if a path has strict permissions</P>
+ * <UL>
+ *
+ * <LI><P>
+ * (For {@code Unix}) The path may not have group or others write permissions
+ * </P></LI>
+ *
+ * <LI><P>
+ * The path must be owned by current user.
+ * </P></LI>
+ *
+ * <LI><P>
+ * (For {@code Unix}) The path may be owned by root.
+ * </P></LI>
+ *
+ * </UL>
+ *
+ * @param path The {@link Path} to be checked - ignored if {@code null}
+ * or does not exist
+ * @param options The {@link LinkOption}s to use to query the file's permissions
+ * @return The violated permission as {@link SimpleImmutableEntry} where key
+ * is a loggable message and value is the offending object
+ * - e.g., {@link PosixFilePermission} or {@link String} for owner. Return
+ * value is {@code null} if no violations detected
+ * @throws IOException If failed to retrieve the permissions
+ * @see #STRICTLY_PROHIBITED_FILE_PERMISSION
+ */
+ public static SimpleImmutableEntry<String, Object> validateStrictConfigFilePermissions(Path path, LinkOption... options) throws IOException {
+ if ((path == null) || (!Files.exists(path, options))) {
+ return null;
+ }
+
+ Collection<PosixFilePermission> perms = IoUtils.getPermissions(path, options);
+ if (GenericUtils.isEmpty(perms)) {
+ return null;
+ }
+
+ if (OsUtils.isUNIX()) {
+ PosixFilePermission p = IoUtils.validateExcludedPermissions(perms, STRICTLY_PROHIBITED_FILE_PERMISSION);
+ if (p != null) {
+ return new SimpleImmutableEntry<>(String.format("Permissions violation (%s)", p), p);
+ }
+ }
+
+ String owner = IoUtils.getFileOwner(path, options);
+ if (GenericUtils.isEmpty(owner)) {
+ // we cannot get owner
+ // general issue: jvm does not support permissions
+ // security issue: specific filesystem does not support permissions
+ return null;
+ }
+
+ String current = OsUtils.getCurrentUser();
+ Set<String> expected = new HashSet<>();
+ expected.add(current);
+ if (OsUtils.isUNIX()) {
+ // Windows "Administrator" was considered however in Windows most likely a group is used.
+ expected.add(OsUtils.ROOT_USER);
+ }
+
+ if (!expected.contains(owner)) {
+ return new SimpleImmutableEntry<>(String.format("Owner violation (%s)", owner), owner);
+ }
+
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
new file mode 100644
index 0000000..b9aedee
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseInputStream.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NoCloseInputStream extends FilterInputStream {
+ public NoCloseInputStream(InputStream in) {
+ super(in);
+ }
+
+ @Override
+ public void close() throws IOException {
+ // ignored
+ }
+
+ public static InputStream resolveInputStream(InputStream input, boolean okToClose) {
+ if ((input == null) || okToClose) {
+ return input;
+ } else {
+ return new NoCloseInputStream(input);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
new file mode 100644
index 0000000..4ba16d3
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseOutputStream.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.io;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NoCloseOutputStream extends FilterOutputStream {
+ public NoCloseOutputStream(OutputStream out) {
+ super(out);
+ }
+
+ @Override
+ public void close() throws IOException {
+ // ignored
+ }
+
+ public static OutputStream resolveOutputStream(OutputStream output, boolean okToClose) {
+ if ((output == null) || okToClose) {
+ return output;
+ } else {
+ return new NoCloseOutputStream(output);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
new file mode 100644
index 0000000..9c8b218
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseReader.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.FilterReader;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NoCloseReader extends FilterReader {
+ public NoCloseReader(Reader in) {
+ super(in);
+ }
+
+ @Override
+ public void close() throws IOException {
+ // ignored
+ }
+
+ public static Reader resolveReader(Reader r, boolean okToClose) {
+ if ((r == null) || okToClose) {
+ return r;
+ } else {
+ return new NoCloseReader(r);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java
new file mode 100644
index 0000000..0f92697
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NoCloseWriter.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.FilterWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NoCloseWriter extends FilterWriter {
+ public NoCloseWriter(Writer out) {
+ super(out);
+ }
+
+ @Override
+ public void close() throws IOException {
+ // ignored
+ }
+
+ public static Writer resolveWriter(Writer r, boolean okToClose) {
+ if ((r == null) || okToClose) {
+ return r;
+ } else {
+ return new NoCloseWriter(r);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java
new file mode 100644
index 0000000..eb21383
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullInputStream.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.Channel;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A {@code /dev/null} input stream
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NullInputStream extends InputStream implements Channel {
+ private final AtomicBoolean open = new AtomicBoolean(true);
+
+ public NullInputStream() {
+ super();
+ }
+
+ @Override
+ public boolean isOpen() {
+ return open.get();
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (!isOpen()) {
+ throw new EOFException("Stream is closed for reading one value");
+ }
+ return -1;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (!isOpen()) {
+ throw new EOFException("Stream is closed for reading " + len + " bytes");
+ }
+ return -1;
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ if (!isOpen()) {
+ throw new EOFException("Stream is closed for skipping " + n + " bytes");
+ }
+ return 0L;
+ }
+
+ @Override
+ public int available() throws IOException {
+ if (!isOpen()) {
+ throw new EOFException("Stream is closed for availability query");
+ }
+ return 0;
+ }
+
+ @Override
+ public synchronized void reset() throws IOException {
+ if (!isOpen()) {
+ throw new EOFException("Stream is closed for reset");
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (open.getAndSet(false)) {
+ //noinspection UnnecessaryReturnStatement
+ return; // debug breakpoint
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.java
new file mode 100644
index 0000000..67fa2d0
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/NullOutputStream.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.util.io;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.channels.Channel;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A {code /dev/null} output stream
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NullOutputStream extends OutputStream implements Channel {
+ private final AtomicBoolean open = new AtomicBoolean(true);
+
+ public NullOutputStream() {
+ super();
+ }
+
+ @Override
+ public boolean isOpen() {
+ return open.get();
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ if (!isOpen()) {
+ throw new EOFException("Stream is closed for writing one byte");
+ }
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ if (!isOpen()) {
+ throw new EOFException("Stream is closed for writing " + len + " bytes");
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ if (!isOpen()) {
+ throw new EOFException("Stream is closed for flushing");
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (open.getAndSet(false)) {
+ //noinspection UnnecessaryReturnStatement
+ return; // debug breakpoint
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.java
new file mode 100644
index 0000000..6f81872
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/OutputStreamWithChannel.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.util.io;
+
+import java.io.OutputStream;
+import java.nio.channels.Channel;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class OutputStreamWithChannel extends OutputStream implements Channel {
+ protected OutputStreamWithChannel() {
+ super();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Class.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Class.java b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Class.java
new file mode 100644
index 0000000..b8351f1
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/io/der/ASN1Class.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.io.der;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public enum ASN1Class {
+ // NOTE: order is crucial, so DON'T change it
+ UNIVERSAL((byte) 0x00),
+ APPLICATION((byte) 0x01),
+ CONTEXT((byte) 0x02),
+ PRIVATE((byte) 0x03);
+
+ public static final List<ASN1Class> VALUES =
+ Collections.unmodifiableList(Arrays.asList(values()));
+
+ private final byte byteValue;
+
+ ASN1Class(byte classValue) {
+ byteValue = classValue;
+ }
+
+ public byte getClassValue() {
+ return byteValue;
+ }
+
+ public static ASN1Class fromName(String s) {
+ if (GenericUtils.isEmpty(s)) {
+ return null;
+ }
+
+ for (ASN1Class c : VALUES) {
+ if (s.equalsIgnoreCase(c.name())) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * <P>The first byte in DER encoding is made of following fields</P>
+ * <pre>
+ *-------------------------------------------------
+ *|Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
+ *-------------------------------------------------
+ *| Class | CF | Type |
+ *-------------------------------------------------
+ * </pre>
+ * @param value The original DER encoded byte
+ * @return The {@link ASN1Class} value - {@code null} if no match found
+ * @see #fromTypeValue(int)
+ */
+ public static ASN1Class fromDERValue(int value) {
+ return fromTypeValue((value >> 6) & 0x03);
+ }
+
+ /**
+ * @param value The "pure" value - unshifted and with no extras
+ * @return The {@link ASN1Class} value - {@code null} if no match found
+ */
+ public static ASN1Class fromTypeValue(int value) {
+ // all 4 values are defined
+ if ((value < 0) || (value >= VALUES.size())) {
+ return null;
+ }
+
+ return VALUES.get(value);
+ }
+}
[46/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/VersionProperties.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/VersionProperties.java b/sshd-common/src/main/java/org/apache/sshd/common/config/VersionProperties.java
new file mode 100644
index 0000000..0e351a8
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/VersionProperties.java
@@ -0,0 +1,98 @@
+/*
+ * 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:dev@mina.apache.org">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-common/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java
new file mode 100644
index 0000000..c03616c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntry.java
@@ -0,0 +1,480 @@
+/*
+ * 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;
+
+/**
+ * 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:dev@mina.apache.org">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 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-common/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java
new file mode 100644
index 0000000..70e5c8b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/BuiltinIdentities.java
@@ -0,0 +1,212 @@
+/*
+ * 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:dev@mina.apache.org">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-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.java
new file mode 100644
index 0000000..064f75c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.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.config.keys;
+
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">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-common/src/main/java/org/apache/sshd/common/config/keys/Identity.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/Identity.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/Identity.java
new file mode 100644
index 0000000..eaec413
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/Identity.java
@@ -0,0 +1,42 @@
+/*
+ * 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:dev@mina.apache.org">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-common/src/main/java/org/apache/sshd/common/config/keys/IdentityResourceLoader.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityResourceLoader.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityResourceLoader.java
new file mode 100644
index 0000000..d826821
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityResourceLoader.java
@@ -0,0 +1,49 @@
+/*
+ * 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:dev@mina.apache.org">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-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
new file mode 100644
index 0000000..fbc3ce7
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/IdentityUtils.java
@@ -0,0 +1,159 @@
+/*
+ * 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:dev@mina.apache.org">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-common/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java
new file mode 100644
index 0000000..4bfbea0
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java
@@ -0,0 +1,190 @@
+/*
+ * 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:dev@mina.apache.org">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);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java
new file mode 100644
index 0000000..b59dbd0
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java
@@ -0,0 +1,310 @@
+/*
+ * 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.StreamCorruptedException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.digest.Digest;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Draw an ASCII-Art representing the fingerprint so human brain can
+ * profit from its built-in pattern recognition ability.
+ * This technique is called "random art" and can be found in some
+ * scientific publications like this original paper:
+ *
+ * "Hash Visualization: a New Technique to improve Real-World Security",
+ * Perrig A. and Song D., 1999, International Workshop on Cryptographic
+ * Techniques and E-Commerce (CrypTEC '99)
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <a href="http://sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf">Original article</a>
+ * @see <a href="http://opensource.apple.com/source/OpenSSH/OpenSSH-175/openssh/key.c">C implementation</a>
+ */
+public class KeyRandomArt {
+ public static final int FLDBASE = 8;
+ public static final int FLDSIZE_Y = FLDBASE + 1;
+ public static final int FLDSIZE_X = FLDBASE * 2 + 1;
+ public static final String AUGMENTATION_STRING = " .o+=*BOX@%&#/^SE";
+
+ private final String algorithm;
+ private final int keySize;
+ private final char[][] field = new char[FLDSIZE_X][FLDSIZE_Y];
+
+ public KeyRandomArt(PublicKey key) throws Exception {
+ this(key, KeyUtils.getDefaultFingerPrintFactory());
+ }
+
+ public KeyRandomArt(PublicKey key, Factory<? extends Digest> f) throws Exception {
+ this(key, Objects.requireNonNull(f, "No digest factory").create());
+ }
+
+ public KeyRandomArt(PublicKey key, Digest d) throws Exception {
+ this(Objects.requireNonNull(key, "No key provided").getAlgorithm(),
+ KeyUtils.getKeySize(key),
+ KeyUtils.getRawFingerprint(Objects.requireNonNull(d, "No key digest"), key));
+ }
+
+ /**
+ * @param algorithm The key algorithm
+ * @param keySize The key size in bits
+ * @param digest The key digest
+ */
+ public KeyRandomArt(String algorithm, int keySize, byte[] digest) {
+ this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm provided");
+ ValidateUtils.checkTrue(keySize > 0, "Invalid key size: %d", keySize);
+ this.keySize = keySize;
+ Objects.requireNonNull(digest, "No key digest provided");
+
+ int x = FLDSIZE_X / 2;
+ int y = FLDSIZE_Y / 2;
+ int len = AUGMENTATION_STRING.length() - 1;
+ for (int i = 0; i < digest.length; i++) {
+ /* each byte conveys four 2-bit move commands */
+ int input = digest[i] & 0xFF;
+ for (int b = 0; b < 4; b++) {
+ /* evaluate 2 bit, rest is shifted later */
+ x += ((input & 0x1) != 0) ? 1 : -1;
+ y += ((input & 0x2) != 0) ? 1 : -1;
+
+ /* assure we are still in bounds */
+ x = Math.max(x, 0);
+ y = Math.max(y, 0);
+ x = Math.min(x, FLDSIZE_X - 1);
+ y = Math.min(y, FLDSIZE_Y - 1);
+
+ /* augment the field */
+ if (field[x][y] < (len - 2)) {
+ field[x][y]++;
+ }
+ input = input >> 2;
+ }
+ }
+
+ /* mark starting point and end point*/
+ field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = (char) (len - 1);
+ field[x][y] = (char) len;
+ }
+
+ public String getAlgorithm() {
+ return algorithm;
+ }
+
+ public int getKeySize() {
+ return keySize;
+ }
+
+ /**
+ * Outputs the generated random art
+ *
+ * @param <A> The {@link Appendable} output writer
+ * @param sb The writer
+ * @return The updated writer instance
+ * @throws IOException If failed to write the combined result
+ */
+ public <A extends Appendable> A append(A sb) throws IOException {
+ // Upper border
+ String s = String.format("+--[%4s %4d]", getAlgorithm(), getKeySize());
+ sb.append(s);
+ for (int index = s.length(); index <= FLDSIZE_X; index++) {
+ sb.append('-');
+ }
+ sb.append('+');
+ sb.append('\n');
+
+ // contents
+ int len = AUGMENTATION_STRING.length() - 1;
+ for (int y = 0; y < FLDSIZE_Y; y++) {
+ sb.append('|');
+ for (int x = 0; x < FLDSIZE_X; x++) {
+ char ch = field[x][y];
+ sb.append(AUGMENTATION_STRING.charAt(Math.min(ch, len)));
+ }
+ sb.append('|');
+ sb.append('\n');
+ }
+
+ // lower border
+ sb.append('+');
+ for (int index = 0; index < FLDSIZE_X; index++) {
+ sb.append('-');
+ }
+
+ sb.append('+');
+ sb.append('\n');
+ return sb;
+ }
+
+ @Override
+ public String toString() {
+ try {
+ return append(new StringBuilder((FLDSIZE_X + 4) * (FLDSIZE_Y + 3))).toString();
+ } catch (IOException e) {
+ return e.getClass().getSimpleName(); // unexpected
+ }
+ }
+
+ /**
+ * Combines the arts in a user-friendly way so they are aligned with each other
+ *
+ * @param separator The separator to use between the arts - if empty char
+ * ('\0') then no separation is done
+ * @param arts The {@link KeyRandomArt}s to combine - ignored if {@code null}/empty
+ * @return The combined result
+ */
+ public static String combine(char separator, Collection<? extends KeyRandomArt> arts) {
+ if (GenericUtils.isEmpty(arts)) {
+ return "";
+ }
+
+ try {
+ return combine(new StringBuilder(arts.size() * (FLDSIZE_X + 4) * (FLDSIZE_Y + 3)), separator, arts).toString();
+ } catch (IOException e) {
+ return e.getClass().getSimpleName(); // unexpected
+ }
+ }
+
+ /**
+ * Creates the combined representation of the random art entries for the provided keys
+ *
+ * @param separator The separator to use between the arts - if empty char
+ * ('\0') then no separation is done
+ * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
+ * or has no keys to provide
+ * @return The combined representation
+ * @throws Exception If failed to extract or combine the entries
+ * @see #combine(Appendable, char, KeyIdentityProvider)
+ */
+ public static String combine(char separator, KeyIdentityProvider provider) throws Exception {
+ return combine(new StringBuilder(4 * (FLDSIZE_X + 4) * (FLDSIZE_Y + 3)), separator, provider).toString();
+ }
+
+ /**
+ * Appends the combined random art entries for the provided keys
+ *
+ * @param <A> The {@link Appendable} output writer
+ * @param sb The writer
+ * @param separator The separator to use between the arts - if empty char
+ * ('\0') then no separation is done
+ * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
+ * or has no keys to provide
+ * @return The updated writer instance
+ * @throws Exception If failed to extract or write the entries
+ * @see #generate(KeyIdentityProvider)
+ * @see #combine(Appendable, char, Collection)
+ */
+ public static <A extends Appendable> A combine(A sb, char separator, KeyIdentityProvider provider) throws Exception {
+ return combine(sb, separator, generate(provider));
+ }
+
+ /**
+ * Extracts and generates random art entries for all key in the provider
+ *
+ * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
+ * or has no keys to provide
+ * @return The extracted {@link KeyRandomArt}s
+ * @throws Exception If failed to extract the entries
+ * @see KeyIdentityProvider#loadKeys()
+ */
+ public static Collection<KeyRandomArt> generate(KeyIdentityProvider provider) throws Exception {
+ Iterable<KeyPair> keys = (provider == null) ? null : provider.loadKeys();
+ Iterator<KeyPair> iter = (keys == null) ? null : keys.iterator();
+ if ((iter == null) || (!iter.hasNext())) {
+ return Collections.emptyList();
+ }
+
+ Collection<KeyRandomArt> arts = new LinkedList<>();
+ do {
+ KeyPair kp = iter.next();
+ KeyRandomArt a = new KeyRandomArt(kp.getPublic());
+ arts.add(a);
+ } while (iter.hasNext());
+
+ return arts;
+ }
+
+ /**
+ * Combines the arts in a user-friendly way so they are aligned with each other
+ *
+ * @param <A> The {@link Appendable} output writer
+ * @param sb The writer
+ * @param separator The separator to use between the arts - if empty char
+ * ('\0') then no separation is done
+ * @param arts The {@link KeyRandomArt}s to combine - ignored if {@code null}/empty
+ * @return The updated writer instance
+ * @throws IOException If failed to write the combined result
+ */
+ public static <A extends Appendable> A combine(A sb, char separator, Collection<? extends KeyRandomArt> arts) throws IOException {
+ if (GenericUtils.isEmpty(arts)) {
+ return sb;
+ }
+
+ List<String[]> allLines = new ArrayList<>(arts.size());
+ int numLines = -1;
+ for (KeyRandomArt a : arts) {
+ String s = a.toString();
+ String[] lines = GenericUtils.split(s, '\n');
+ if (numLines <= 0) {
+ numLines = lines.length;
+ } else {
+ if (numLines != lines.length) {
+ throw new StreamCorruptedException("Mismatched lines count: expected=" + numLines + ", actual=" + lines.length);
+ }
+ }
+
+ for (int index = 0; index < lines.length; index++) {
+ String l = lines[index];
+ if ((l.length() > 0) && (l.charAt(l.length() - 1) == '\r')) {
+ l = l.substring(0, l.length() - 1);
+ lines[index] = l;
+ }
+ }
+
+ allLines.add(lines);
+ }
+
+ for (int row = 0; row < numLines; row++) {
+ for (int index = 0; index < allLines.size(); index++) {
+ String[] lines = allLines.get(index);
+ String l = lines[row];
+ sb.append(l);
+ if ((index > 0) && (separator != '\0')) {
+ sb.append(separator);
+ }
+ }
+ sb.append('\n');
+ }
+
+ return sb;
+ }
+}
[19/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java b/sshd-core/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
deleted file mode 100644
index f469583..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/BuiltinDigests.java
+++ /dev/null
@@ -1,166 +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.digest;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Set;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * Provides easy access to the currently implemented digests
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public enum BuiltinDigests implements DigestFactory {
- md5(Constants.MD5, "MD5", 16),
- sha1(Constants.SHA1, "SHA-1", 20),
- sha224(Constants.SHA224, "SHA-224", 28),
- sha256(Constants.SHA256, "SHA-256", 32),
- sha384(Constants.SHA384, "SHA-384", 48),
- sha512(Constants.SHA512, "SHA-512", 64);
-
- public static final Set<BuiltinDigests> VALUES =
- Collections.unmodifiableSet(EnumSet.allOf(BuiltinDigests.class));
-
- private final String algorithm;
- private final int blockSize;
- private final String factoryName;
- private final boolean supported;
-
- BuiltinDigests(String factoryName, String algorithm, int blockSize) {
- this.factoryName = factoryName;
- this.algorithm = algorithm;
- this.blockSize = blockSize;
- /*
- * 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 = DigestUtils.checkSupported(algorithm);
- }
-
- @Override
- public final String getName() {
- return factoryName;
- }
-
- @Override
- public final String getAlgorithm() {
- return algorithm;
- }
-
- @Override
- public final int getBlockSize() {
- return blockSize;
- }
-
- @Override
- public final String toString() {
- return getName();
- }
-
- @Override
- public final Digest create() {
- return new BaseDigest(getAlgorithm(), getBlockSize());
- }
-
- @Override
- public final boolean isSupported() {
- return supported;
- }
-
- /**
- * @param s The {@link Enum}'s name - ignored if {@code null}/empty
- * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose {@link Enum#name()} matches
- * (case <U>insensitive</U>) the provided argument - {@code null} if no match
- */
- public static BuiltinDigests fromString(String s) {
- if (GenericUtils.isEmpty(s)) {
- return null;
- }
-
- for (BuiltinDigests c : VALUES) {
- if (s.equalsIgnoreCase(c.name())) {
- return c;
- }
- }
-
- return null;
- }
-
- /**
- * @param factory The {@link org.apache.sshd.common.NamedFactory} for the cipher - ignored if {@code null}
- * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose factory name matches
- * (case <U>insensitive</U>) the digest factory name
- * @see #fromFactoryName(String)
- */
- public static BuiltinDigests fromFactory(NamedFactory<? extends Digest> 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 org.apache.sshd.common.digest.BuiltinDigests} whose factory name matches
- * (case <U>insensitive</U>) the provided name - {@code null} if no match
- */
- public static BuiltinDigests fromFactoryName(String name) {
- return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
- }
-
- /**
- * @param d The {@link Digest} instance - ignored if {@code null}
- * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose algorithm matches
- * (case <U>insensitive</U>) the digets's algorithm - {@code null} if no match
- */
- public static BuiltinDigests fromDigest(Digest d) {
- return fromAlgorithm((d == null) ? null : d.getAlgorithm());
- }
-
- /**
- * @param algo The algorithm to find - ignored if {@code null}/empty
- * @return The matching {@link org.apache.sshd.common.digest.BuiltinDigests} whose algorithm matches
- * (case <U>insensitive</U>) the provided name - {@code null} if no match
- */
- public static BuiltinDigests fromAlgorithm(String algo) {
- return DigestUtils.findFactoryByAlgorithm(algo, String.CASE_INSENSITIVE_ORDER, VALUES);
- }
-
- public static final class Constants {
- public static final String MD5 = "md5";
- public static final String SHA1 = "sha1";
- public static final String SHA224 = "sha224";
- public static final String SHA256 = "sha256";
- public static final String SHA384 = "sha384";
- public static final String SHA512 = "sha512";
-
- 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/digest/Digest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/digest/Digest.java b/sshd-core/src/main/java/org/apache/sshd/common/digest/Digest.java
deleted file mode 100644
index 27204ff..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/Digest.java
+++ /dev/null
@@ -1,36 +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.digest;
-
-/**
- * Interface used to compute digests, based on algorithms such as MD5 or SHA1.
- * The digest implementation are compared first by the algorithm name (case
- * <U>insensitive</U> and second according to the block size
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Digest extends DigestInformation, Comparable<Digest> {
- void init() throws Exception;
-
- void update(byte[] data) throws Exception;
-
- void update(byte[] data, int start, int len) throws Exception;
-
- byte[] digest() throws Exception;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestFactory.java
deleted file mode 100644
index f00e7ba..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestFactory.java
+++ /dev/null
@@ -1,32 +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.digest;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.OptionalFeature;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-// CHECKSTYLE:OFF
-public interface DigestFactory extends DigestInformation, NamedFactory<Digest>, OptionalFeature {
- // nothing extra
-}
-// CHECKSTYLE:ON
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestInformation.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestInformation.java b/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestInformation.java
deleted file mode 100644
index ba181dc..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestInformation.java
+++ /dev/null
@@ -1,36 +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.digest;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface DigestInformation {
- /**
- * @return The digest algorithm name
- */
- String getAlgorithm();
-
- /**
- * @return The number of bytes in the digest's output
- */
- int getBlockSize();
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestUtils.java
deleted file mode 100644
index 30369ac..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/DigestUtils.java
+++ /dev/null
@@ -1,228 +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.digest;
-
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import java.util.Base64;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Objects;
-
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class DigestUtils {
- private DigestUtils() {
- throw new UnsupportedOperationException("No instance");
- }
-
- /**
- * @param algorithm The digest algorithm - never {@code null}/empty
- * @return {@code true} if this digest algorithm is supported
- * @see SecurityUtils#getMessageDigest(String)
- */
- public static boolean checkSupported(String algorithm) {
- ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm");
- try {
- MessageDigest digest = SecurityUtils.getMessageDigest(algorithm);
- return digest != null; // just in case
- } catch (Exception e) {
- return false;
- }
- }
-
- /**
- * @param <D> The generic type of digest factory
- * @param algo The required algorithm name - ignored if {@code null}/empty
- * @param comp The {@link Comparator} to use to compare algorithm names
- * @param digests The factories to check - ignored if {@code null}/empty
- * @return The first {@link DigestFactory} whose algorithm matches the required one
- * according to the comparator - {@code null} if no match found
- */
- public static <D extends Digest> D findDigestByAlgorithm(String algo, Comparator<? super String> comp, Collection<? extends D> digests) {
- if (GenericUtils.isEmpty(algo) || GenericUtils.isEmpty(digests)) {
- return null;
- }
-
- for (D d : digests) {
- if (comp.compare(algo, d.getAlgorithm()) == 0) {
- return d;
- }
- }
-
- return null;
- }
-
- /**
- * @param <F> The generic type of digest factory
- * @param algo The required algorithm name - ignored if {@code null}/empty
- * @param comp The {@link Comparator} to use to compare algorithm names
- * @param factories The factories to check - ignored if {@code null}/empty
- * @return The first {@link DigestFactory} whose algorithm matches the required one
- * according to the comparator - {@code null} if no match found
- */
- public static <F extends DigestFactory> F findFactoryByAlgorithm(String algo, Comparator<? super String> comp, Collection<? extends F> factories) {
- if (GenericUtils.isEmpty(algo) || GenericUtils.isEmpty(factories)) {
- return null;
- }
-
- for (F f : factories) {
- if (comp.compare(algo, f.getAlgorithm()) == 0) {
- return f;
- }
- }
-
- return null;
- }
-
- /**
- * @param f The {@link Factory} to create the {@link Digest} to use
- * @param s The {@link String} to digest - ignored if {@code null}/empty,
- * otherwise its UTF-8 representation is used as input for the fingerprint
- * @return The fingerprint - {@code null} if {@code null}/empty input
- * @throws Exception If failed to calculate the digest
- * @see #getFingerPrint(Digest, String, Charset)
- */
- public static String getFingerPrint(Factory<? extends Digest> f, String s) throws Exception {
- return getFingerPrint(f, s, StandardCharsets.UTF_8);
- }
-
- /**
- * @param f The {@link Factory} to create the {@link Digest} to use
- * @param s The {@link String} to digest - ignored if {@code null}/empty
- * @param charset The {@link Charset} to use in order to convert the
- * string to its byte representation to use as input for the fingerprint
- * @return The fingerprint - {@code null} if {@code null}/empty input
- * @throws Exception If failed to calculate the digest
- */
- public static String getFingerPrint(Factory<? extends Digest> f, String s, Charset charset) throws Exception {
- return getFingerPrint(Objects.requireNonNull(f, "No factory").create(), s, charset);
- }
-
- /**
- * @param d The {@link Digest} to use
- * @param s The {@link String} to digest - ignored if {@code null}/empty,
- * otherwise its UTF-8 representation is used as input for the fingerprint
- * @return The fingerprint - {@code null} if {@code null}/empty input
- * @throws Exception If failed to calculate the digest
- * @see #getFingerPrint(Digest, String, Charset)
- */
- public static String getFingerPrint(Digest d, String s) throws Exception {
- return getFingerPrint(d, s, StandardCharsets.UTF_8);
- }
-
- /**
- * @param d The {@link Digest} to use
- * @param s The {@link String} to digest - ignored if {@code null}/empty
- * @param charset The {@link Charset} to use in order to convert the
- * string to its byte representation to use as input for the fingerprint
- * @return The fingerprint - {@code null} if {@code null}/empty input
- * @throws Exception If failed to calculate the digest
- */
- public static String getFingerPrint(Digest d, String s, Charset charset) throws Exception {
- if (GenericUtils.isEmpty(s)) {
- return null;
- } else {
- return DigestUtils.getFingerPrint(d, s.getBytes(charset));
- }
- }
-
- /**
- * @param f The {@link Factory} to create the {@link Digest} to use
- * @param buf The data buffer to be fingerprint-ed
- * @return The fingerprint - {@code null} if empty data buffer
- * @throws Exception If failed to calculate the fingerprint
- * @see #getFingerPrint(Factory, byte[], int, int)
- */
- public static String getFingerPrint(Factory<? extends Digest> f, byte... buf) throws Exception {
- return getFingerPrint(f, buf, 0, NumberUtils.length(buf));
- }
-
- /**
- * @param f The {@link Factory} to create the {@link Digest} to use
- * @param buf The data buffer to be fingerprint-ed
- * @param offset The offset of the data in the buffer
- * @param len The length of data - ignored if non-positive
- * @return The fingerprint - {@code null} if non-positive length
- * @throws Exception If failed to calculate the fingerprint
- */
- public static String getFingerPrint(Factory<? extends Digest> f, byte[] buf, int offset, int len) throws Exception {
- return getFingerPrint(Objects.requireNonNull(f, "No factory").create(), buf, offset, len);
- }
-
- /**
- * @param d The {@link Digest} to use
- * @param buf The data buffer to be fingerprint-ed
- * @return The fingerprint - {@code null} if empty data buffer
- * @throws Exception If failed to calculate the fingerprint
- * @see #getFingerPrint(Digest, byte[], int, int)
- */
- public static String getFingerPrint(Digest d, byte... buf) throws Exception {
- return getFingerPrint(d, buf, 0, NumberUtils.length(buf));
- }
-
- /**
- * @param d The {@link Digest} to use
- * @param buf The data buffer to be fingerprint-ed
- * @param offset The offset of the data in the buffer
- * @param len The length of data - ignored if non-positive
- * @return The fingerprint - {@code null} if non-positive length
- * @throws Exception If failed to calculate the fingerprint
- * @see #getRawFingerprint(Digest, byte[], int, int)
- */
- public static String getFingerPrint(Digest d, byte[] buf, int offset, int len) throws Exception {
- if (len <= 0) {
- return null;
- }
-
- byte[] data = getRawFingerprint(d, buf, offset, len);
- String algo = d.getAlgorithm();
- if (BuiltinDigests.md5.getAlgorithm().equals(algo)) {
- return algo + ":" + BufferUtils.toHex(':', data).toLowerCase();
- }
-
- Base64.Encoder encoder = Base64.getEncoder();
- return algo.replace("-", "").toUpperCase() + ":" + encoder.encodeToString(data).replaceAll("=", "");
- }
-
- public static byte[] getRawFingerprint(Digest d, byte... buf) throws Exception {
- return getRawFingerprint(d, buf, 0, NumberUtils.length(buf));
- }
-
- public static byte[] getRawFingerprint(Digest d, byte[] buf, int offset, int len) throws Exception {
- if (len <= 0) {
- return null;
- }
-
- Objects.requireNonNull(d, "No digest").init();
- d.update(buf, offset, len);
-
- return d.digest();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/digest/package.html
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/digest/package.html b/sshd-core/src/main/java/org/apache/sshd/common/digest/package.html
deleted file mode 100644
index 65940e0..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/package.html
+++ /dev/null
@@ -1,25 +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.
--->
-<html>
-<head>
-</head>
-<body>
-
-<a href="{@docRoot}/org/apache/sshd/common/digest/Digest.html"><code>Digest</code></a> implementations.
-
-</body>
-</html>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java
deleted file mode 100644
index 1dca54c..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/AbstractSshFuture.java
+++ /dev/null
@@ -1,194 +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.future;
-
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.io.StreamCorruptedException;
-import java.util.function.Function;
-
-import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * @param <T> Type of future
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractSshFuture<T extends SshFuture> extends AbstractLoggingBean implements SshFuture<T> {
- /**
- * A default value to indicate the future has been canceled
- */
- protected static final Object CANCELED = new Object();
-
- protected final boolean debugEnabled;
- protected final boolean traceEnabled;
- private final Object id;
-
- /**
- * @param id Some identifier useful as {@link #toString()} value
- */
- protected AbstractSshFuture(Object id) {
- this.id = id;
- this.debugEnabled = log.isDebugEnabled();
- this.traceEnabled = log.isTraceEnabled();
- }
-
- @Override
- public Object getId() {
- return id;
- }
-
- @Override
- public boolean await(long timeoutMillis) throws IOException {
- return await0(timeoutMillis, true) != null;
- }
-
- @Override
- public boolean awaitUninterruptibly(long timeoutMillis) {
- try {
- return await0(timeoutMillis, false) != null;
- } catch (InterruptedIOException e) {
- throw formatExceptionMessage(msg -> new InternalError(msg, e),
- "Unexpected interrupted exception wile awaitUninterruptibly %d msec: %s",
- timeoutMillis, e.getMessage());
- }
- }
-
- /**
- * <P>Waits (interruptible) for the specified timeout (msec.) and then checks
- * the result:</P>
- * <UL>
- * <LI><P>
- * If result is {@code null} then timeout is assumed to have expired - throw
- * an appropriate {@link IOException}
- * </P></LI>
- *
- * <LI><P>
- * If the result is of the expected type, then cast and return it
- * </P></LI>
- *
- * <LI><P>
- * If the result is an {@link IOException} then re-throw it
- * </P></LI>
- *
- * <LI><P>
- * If the result is a {@link Throwable} then throw an {@link IOException}
- * whose cause is the original exception
- * </P></LI>
- *
- * <LI><P>
- * Otherwise (should never happen), throw a {@link StreamCorruptedException}
- * with the name of the result type
- * </P></LI>
- * </UL>
- *
- * @param <R> The generic result type
- * @param expectedType The expected result type
- * @param timeout The timeout (millis) to wait for a result
- * @return The (never {@code null}) result
- * @throws IOException If failed to retrieve the expected result on time
- */
- protected <R> R verifyResult(Class<? extends R> expectedType, long timeout) throws IOException {
- Object value = await0(timeout, true);
- if (value == null) {
- throw formatExceptionMessage(SshException::new, "Failed to get operation result within specified timeout: %s", timeout);
- }
-
- Class<?> actualType = value.getClass();
- if (expectedType.isAssignableFrom(actualType)) {
- return expectedType.cast(value);
- }
-
- if (Throwable.class.isAssignableFrom(actualType)) {
- Throwable t = GenericUtils.peelException((Throwable) value);
- if (t != value) {
- value = t;
- actualType = value.getClass();
- }
-
- if (IOException.class.isAssignableFrom(actualType)) {
- throw (IOException) value;
- }
-
- Throwable cause = GenericUtils.resolveExceptionCause(t);
- throw formatExceptionMessage(msg -> new SshException(msg, cause), "Failed (%s) to execute: %s", t.getClass().getSimpleName(), t.getMessage());
- } else { // what else can it be ????
- throw formatExceptionMessage(StreamCorruptedException::new, "Unknown result type: %s", actualType.getName());
- }
- }
-
- /**
- * Wait for the Future to be ready. If the requested delay is 0 or
- * negative, this method returns immediately.
- *
- * @param timeoutMillis The delay we will wait for the Future to be ready
- * @param interruptable Tells if the wait can be interrupted or not.
- * If {@code true} and the thread is interrupted then an {@link InterruptedIOException}
- * is thrown.
- * @return The non-{@code null} result object if the Future is ready,
- * {@code null} if the timeout expired and no result was received
- * @throws InterruptedIOException If the thread has been interrupted when it's not allowed.
- */
- protected abstract Object await0(long timeoutMillis, boolean interruptable) throws InterruptedIOException;
-
- @SuppressWarnings("unchecked")
- protected SshFutureListener<T> asListener(Object o) {
- return (SshFutureListener<T>) o;
- }
-
- protected void notifyListener(SshFutureListener<T> l) {
- try {
- l.operationComplete(asT());
- } catch (Throwable t) {
- log.warn("notifyListener({}) failed ({}) to invoke {}: {}",
- this, t.getClass().getSimpleName(), l, t.getMessage());
- if (debugEnabled) {
- log.debug("notifyListener(" + this + ")[" + l + "] invocation failure details", t);
- }
- }
- }
-
- @SuppressWarnings("unchecked")
- protected T asT() {
- return (T) this;
- }
-
- /**
- * Generates an exception whose message is prefixed by the future simple class name + {@link #getId() identifier}
- * as a hint to the context of the failure.
- *
- * @param <E> Type of {@link Throwable} being generated
- * @param exceptionCreator The exception creator from the formatted message
- * @param format The extra payload format as per {@link String#format(String, Object...)}
- * @param args The formatting arguments
- * @return The generated exception
- */
- protected <E extends Throwable> E formatExceptionMessage(Function<? super String, ? extends E> exceptionCreator, String format, Object... args) {
- String messagePayload = String.format(format, args);
- String excMessage = getClass().getSimpleName() + "[" + getId() + "]: " + messagePayload;
- return exceptionCreator.apply(excMessage);
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + "[id=" + getId() + "]";
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/CloseFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/CloseFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/CloseFuture.java
deleted file mode 100644
index 808716d..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/CloseFuture.java
+++ /dev/null
@@ -1,40 +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.future;
-
-/**
- * An {@link SshFuture} for asynchronous close requests.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface CloseFuture extends SshFuture<CloseFuture> {
-
- /**
- * @return <tt>true</tt> if the close request is finished and the target is closed.
- */
- boolean isClosed();
-
- /**
- * Marks this future as closed and notifies all threads waiting for this
- * future. This method is invoked by SSHD internally. Please do not call
- * this method directly.
- */
- void setClosed();
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java
deleted file mode 100644
index 4c34a06..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultCloseFuture.java
+++ /dev/null
@@ -1,52 +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.future;
-
-/**
- * A default implementation of {@link CloseFuture}.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DefaultCloseFuture extends DefaultSshFuture<CloseFuture> implements CloseFuture {
-
- /**
- * Create a new instance
- *
- * @param id Some identifier useful as {@link #toString()} value
- * @param lock A synchronization object for locking access - if {@code null}
- * then synchronization occurs on {@code this} instance
- */
- public DefaultCloseFuture(Object id, Object lock) {
- super(id, lock);
- }
-
- @Override
- public boolean isClosed() {
- if (isDone()) {
- return (Boolean) getValue();
- } else {
- return false;
- }
- }
-
- @Override
- public void setClosed() {
- setValue(Boolean.TRUE);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
deleted file mode 100644
index cd475ff..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultSshFuture.java
+++ /dev/null
@@ -1,236 +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.future;
-
-import java.io.InterruptedIOException;
-import java.lang.reflect.Array;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * A default implementation of {@link SshFuture}.
- *
- * @param <T> Type of future
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DefaultSshFuture<T extends SshFuture> extends AbstractSshFuture<T> {
- /**
- * A lock used by the wait() method
- */
- private final Object lock;
- private Object listeners;
- private Object result;
-
- /**
- * Creates a new instance.
- *
- * @param id Some identifier useful as {@link #toString()} value
- * @param lock A synchronization object for locking access - if {@code null}
- * then synchronization occurs on {@code this} instance
- */
- public DefaultSshFuture(Object id, Object lock) {
- super(id);
-
- this.lock = (lock != null) ? lock : this;
- }
-
- @Override
- protected Object await0(long timeoutMillis, boolean interruptable) throws InterruptedIOException {
- ValidateUtils.checkTrue(timeoutMillis >= 0L, "Negative timeout N/A: %d", timeoutMillis);
- long startTime = System.currentTimeMillis();
- long curTime = startTime;
- long endTime = ((Long.MAX_VALUE - timeoutMillis) < curTime) ? Long.MAX_VALUE : (curTime + timeoutMillis);
-
- synchronized (lock) {
- if ((result != null) || (timeoutMillis <= 0)) {
- return result;
- }
-
- for (;;) {
- try {
- lock.wait(endTime - curTime);
- } catch (InterruptedException e) {
- if (interruptable) {
- curTime = System.currentTimeMillis();
- throw formatExceptionMessage(msg -> {
- InterruptedIOException exc = new InterruptedIOException(msg);
- exc.initCause(e);
- return exc;
- }, "Interrupted after %d msec.", curTime - startTime);
- }
- }
-
- curTime = System.currentTimeMillis();
- if ((result != null) || (curTime >= endTime)) {
- return result;
- }
- }
- }
- }
-
- @Override
- public boolean isDone() {
- synchronized (lock) {
- return result != null;
- }
- }
-
- /**
- * Sets the result of the asynchronous operation, and mark it as finished.
- *
- * @param newValue The operation result
- */
- public void setValue(Object newValue) {
- synchronized (lock) {
- // Allow only once.
- if (result != null) {
- return;
- }
-
- result = (newValue != null) ? newValue : GenericUtils.NULL;
- lock.notifyAll();
- }
-
- notifyListeners();
- }
-
- public int getNumRegisteredListeners() {
- synchronized (lock) {
- if (listeners == null) {
- return 0;
- } else if (listeners instanceof SshFutureListener) {
- return 1;
- } else {
- int l = Array.getLength(listeners);
- int count = 0;
- for (int i = 0; i < l; i++) {
- if (Array.get(listeners, i) != null) {
- count++;
- }
- }
- return count;
- }
- }
- }
-
- /**
- * @return The result of the asynchronous operation - or {@code null}
- * if none set.
- */
- public Object getValue() {
- synchronized (lock) {
- return (result == GenericUtils.NULL) ? null : result;
- }
- }
-
- @Override
- public T addListener(SshFutureListener<T> listener) {
- Objects.requireNonNull(listener, "Missing listener argument");
- boolean notifyNow = false;
- synchronized (lock) {
- // if already have a result don't register the listener and invoke it directly
- if (result != null) {
- notifyNow = true;
- } else if (listeners == null) {
- listeners = listener; // 1st listener ?
- } else if (listeners instanceof SshFutureListener) {
- listeners = new Object[]{listeners, listener};
- } else { // increase array of registered listeners
- Object[] ol = (Object[]) listeners;
- int l = ol.length;
- Object[] nl = new Object[l + 1];
- System.arraycopy(ol, 0, nl, 0, l);
- nl[l] = listener;
- listeners = nl;
- }
- }
-
- if (notifyNow) {
- notifyListener(listener);
- }
-
- return asT();
- }
-
- @Override
- public T removeListener(SshFutureListener<T> listener) {
- Objects.requireNonNull(listener, "No listener provided");
-
- synchronized (lock) {
- if (result != null) {
- return asT(); // the train has already left the station...
- }
-
- if (listeners == null) {
- return asT(); // no registered instances anyway
- }
-
- if (listeners == listener) {
- listeners = null; // the one and only
- } else if (!(listeners instanceof SshFutureListener)) {
- int l = Array.getLength(listeners);
- for (int i = 0; i < l; i++) {
- if (Array.get(listeners, i) == listener) {
- Array.set(listeners, i, null);
- break;
- }
- }
- }
- }
-
- return asT();
- }
-
- protected void notifyListeners() {
- /*
- * There won't be any visibility problem or concurrent modification
- * because result value is checked in both addListener and
- * removeListener calls under lock. If the result is already set then
- * both methods will not modify the internal listeners
- */
- if (listeners != null) {
- if (listeners instanceof SshFutureListener) {
- notifyListener(asListener(listeners));
- } else {
- int l = Array.getLength(listeners);
- for (int i = 0; i < l; i++) {
- SshFutureListener<T> listener = asListener(Array.get(listeners, i));
- if (listener != null) {
- notifyListener(listener);
- }
- }
- }
- }
- }
-
- public boolean isCanceled() {
- return getValue() == CANCELED;
- }
-
- public void cancel() {
- setValue(CANCELED);
- }
-
- @Override
- public String toString() {
- return super.toString() + "[value=" + result + "]";
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultVerifiableSshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultVerifiableSshFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultVerifiableSshFuture.java
deleted file mode 100644
index 1b02fed..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/DefaultVerifiableSshFuture.java
+++ /dev/null
@@ -1,30 +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.future;
-
-/**
- * @param <T> Type of future
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class DefaultVerifiableSshFuture<T extends SshFuture> extends DefaultSshFuture<T> implements VerifiableFuture<T> {
- protected DefaultVerifiableSshFuture(Object id, Object lock) {
- super(id, lock);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/SshFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/SshFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/SshFuture.java
deleted file mode 100644
index b0d8e3c..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/SshFuture.java
+++ /dev/null
@@ -1,48 +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.future;
-
-/**
- * Represents the completion of an asynchronous SSH operation on a given object
- * (it may be an SSH session or an SSH channel).
- * Can be listened for completion using a {@link SshFutureListener}.
- *
- * @param <T> Type of future
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface SshFuture<T extends SshFuture> extends WaitableFuture {
- /**
- * Adds an event <tt>listener</tt> which is notified when
- * this future is completed. If the listener is added
- * after the completion, the listener is directly notified.
- *
- * @param listener The {@link SshFutureListener} instance to add
- * @return The future instance
- */
- T addListener(SshFutureListener<T> listener);
-
- /**
- * Removes an existing event <tt>listener</tt> so it won't be notified when
- * the future is completed.
- *
- * @param listener The {@link SshFutureListener} instance to remove
- * @return The future instance
- */
- T removeListener(SshFutureListener<T> listener);
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/SshFutureListener.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/SshFutureListener.java b/sshd-core/src/main/java/org/apache/sshd/common/future/SshFutureListener.java
deleted file mode 100644
index a005c0f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/SshFutureListener.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.future;
-
-import org.apache.sshd.common.util.SshdEventListener;
-
-/**
- * Something interested in being notified when the completion
- * of an asynchronous SSH operation : {@link SshFuture}.
- *
- * @param <T> type of future
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@SuppressWarnings("rawtypes")
-@FunctionalInterface
-public interface SshFutureListener<T extends SshFuture> extends SshdEventListener {
-
- /**
- * Invoked when the operation associated with the {@link SshFuture}
- * has been completed even if you add the listener after the completion.
- *
- * @param future The source {@link SshFuture} which called this
- * callback.
- */
- void operationComplete(T future);
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/VerifiableFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/VerifiableFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/VerifiableFuture.java
deleted file mode 100644
index e318de7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/VerifiableFuture.java
+++ /dev/null
@@ -1,68 +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.future;
-
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Represents an asynchronous operation whose successful result can be
- * verified somehow. The contract guarantees that if the {@code verifyXXX}
- * method returns without an exception then the operation was completed
- * <U>successfully</U>
- *
- * @param <T> Type of verification result
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface VerifiableFuture<T> {
- /**
- * Wait {@link Long#MAX_VALUE} msec. and verify that the operation was successful
- *
- * @return The (same) future instance
- * @throws IOException If failed to verify successfully on time
- * @see #verify(long)
- */
- default T verify() throws IOException {
- return verify(Long.MAX_VALUE);
- }
-
- /**
- * Wait and verify that the operation was successful
- *
- * @param timeout The number of time units to wait
- * @param unit The wait {@link TimeUnit}
- * @return The (same) future instance
- * @throws IOException If failed to verify successfully on time
- * @see #verify(long)
- */
- default T verify(long timeout, TimeUnit unit) throws IOException {
- return verify(unit.toMillis(timeout));
- }
-
- /**
- * Wait and verify that the operation was successful
- *
- * @param timeoutMillis Wait timeout in milliseconds
- * @return The (same) future instance
- * @throws IOException If failed to verify successfully on time
- */
- T verify(long timeoutMillis) throws IOException;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/future/WaitableFuture.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/future/WaitableFuture.java b/sshd-core/src/main/java/org/apache/sshd/common/future/WaitableFuture.java
deleted file mode 100644
index aff4adc..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/future/WaitableFuture.java
+++ /dev/null
@@ -1,118 +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.future;
-
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Represents an asynchronous operation which one can wait for its completion.
- * <B>Note:</B> the only thing guaranteed is that if {@code true} is returned
- * from one of the {@code awaitXXX} methods then the operation has completed.
- * However, the <B>caller</B> has to determine whether it was a successful or
- * failed completion.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface WaitableFuture {
- /**
- * @return Some identifier useful as {@link #toString()} value
- */
- Object getId();
-
- /**
- * Wait {@link Long#MAX_VALUE} msec. for the asynchronous operation to complete.
- * The attached listeners will be notified when the operation is
- * completed.
- *
- * @return {@code true} if the operation is completed.
- * @throws IOException if failed - specifically {@link java.io.InterruptedIOException}
- * if waiting was interrupted
- * @see #await(long)
- */
- default boolean await() throws IOException {
- return await(Long.MAX_VALUE);
- }
-
- /**
- * Wait for the asynchronous operation to complete with the specified timeout.
- *
- * @param timeout The number of time units to wait
- * @param unit The {@link TimeUnit} for waiting
- * @return {@code true} if the operation is completed.
- * @throws IOException if failed - specifically {@link java.io.InterruptedIOException}
- * if waiting was interrupted
- * @see #await(long)
- */
- default boolean await(long timeout, TimeUnit unit) throws IOException {
- return await(unit.toMillis(timeout));
- }
-
- /**
- * Wait for the asynchronous operation to complete with the specified timeout.
- *
- * @param timeoutMillis Wait time in milliseconds
- * @return {@code true} if the operation is completed.
- * @throws IOException if failed - specifically {@link java.io.InterruptedIOException}
- * if waiting was interrupted
- */
- boolean await(long timeoutMillis) throws IOException;
-
- /**
- * Wait {@link Long#MAX_VALUE} msec. for the asynchronous operation to complete
- * uninterruptibly. The attached listeners will be notified when the operation is
- * completed.
- *
- * @return {@code true} if the operation is completed.
- * @see #awaitUninterruptibly(long)
- */
- default boolean awaitUninterruptibly() {
- return awaitUninterruptibly(Long.MAX_VALUE);
- }
-
- /**
- * Wait for the asynchronous operation to complete with the specified timeout
- * uninterruptibly.
- *
- * @param timeout The number of time units to wait
- * @param unit The {@link TimeUnit} for waiting
- * @return {@code true} if the operation is completed.
- * @see #awaitUninterruptibly(long)
- */
- default boolean awaitUninterruptibly(long timeout, TimeUnit unit) {
- return awaitUninterruptibly(unit.toMillis(timeout));
- }
-
- /**
- * Wait for the asynchronous operation to complete with the specified timeout
- * uninterruptibly.
- *
- * @param timeoutMillis Wait time in milliseconds
- * @return {@code true} if the operation is finished.
- */
- boolean awaitUninterruptibly(long timeoutMillis);
-
- /**
- * @return {@code true} if the asynchronous operation is completed. <B>Note:</B>
- * it is up to the <B>caller</B> to determine whether it was a successful or
- * failed completion.
- */
- boolean isDone();
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
index 7dd7f86..2342e45 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
@@ -29,7 +29,6 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.apache.sshd.agent.SshAgentFactory;
-import org.apache.sshd.common.AttributeStore;
import org.apache.sshd.common.Factory;
import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.NamedFactory;
@@ -165,11 +164,6 @@ public abstract class AbstractFactoryManager extends AbstractKexFactoryManager i
}
@Override
- public <T> T resolveAttribute(AttributeKey<T> key) {
- return AttributeStore.resolveAttribute(this, key);
- }
-
- @Override
public PropertyResolver getParentPropertyResolver() {
return parentResolver;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java
deleted file mode 100644
index 077d199..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractKeyPairProvider.java
+++ /dev/null
@@ -1,32 +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.keyprovider;
-
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * Provides a default implementation for some {@link KeyPairProvider} methods
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractKeyPairProvider extends AbstractLoggingBean implements KeyPairProvider {
- protected AbstractKeyPairProvider() {
- super();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
deleted file mode 100644
index e4e941d..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
+++ /dev/null
@@ -1,234 +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.keyprovider;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Objects;
-import java.util.TreeMap;
-import java.util.TreeSet;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @param <R> Type of resource from which the {@link KeyPair} is generated
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractResourceKeyPairProvider<R> extends AbstractKeyPairProvider {
- private FilePasswordProvider passwordFinder;
- /*
- * NOTE: the map is case insensitive even for Linux, as it is (very) bad
- * practice to have 2 key files that differ from one another only in their
- * case...
- */
- private final Map<String, KeyPair> cacheMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
- protected AbstractResourceKeyPairProvider() {
- super();
- }
-
- public FilePasswordProvider getPasswordFinder() {
- return passwordFinder;
- }
-
- public void setPasswordFinder(FilePasswordProvider passwordFinder) {
- this.passwordFinder = passwordFinder;
- }
-
- /**
- * Checks which of the new resources we already loaded and can keep the
- * associated key pair
- *
- * @param resources The collection of new resources - can be {@code null}/empty
- * in which case the cache is cleared
- */
- protected void resetCacheMap(Collection<?> resources) {
- // if have any cached pairs then see what we can keep from previous load
- Collection<String> toDelete = Collections.emptySet();
- synchronized (cacheMap) {
- if (cacheMap.size() <= 0) {
- return; // already empty - nothing to keep
- }
-
- if (GenericUtils.isEmpty(resources)) {
- cacheMap.clear();
- return;
- }
-
- for (Object r : resources) {
- String resourceKey = ValidateUtils.checkNotNullAndNotEmpty(Objects.toString(r, null), "No resource key value");
- if (cacheMap.containsKey(resourceKey)) {
- continue;
- }
-
- if (toDelete.isEmpty()) {
- toDelete = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
- }
-
- if (!toDelete.add(resourceKey)) {
- continue; // debug breakpoint
- }
- }
-
- if (GenericUtils.size(toDelete) > 0) {
- toDelete.forEach(cacheMap::remove);
- }
- }
-
- if (log.isDebugEnabled()) {
- log.debug("resetCacheMap(" + resources + ") removed previous cached keys for " + toDelete);
- }
- }
-
- protected Iterable<KeyPair> loadKeys(final Collection<? extends R> resources) {
- if (GenericUtils.isEmpty(resources)) {
- return Collections.emptyList();
- } else {
- return () -> new KeyPairIterator(resources);
- }
- }
-
- protected KeyPair doLoadKey(R resource) throws IOException, GeneralSecurityException {
- String resourceKey = ValidateUtils.checkNotNullAndNotEmpty(Objects.toString(resource, null), "No resource string value");
- KeyPair kp;
- synchronized (cacheMap) {
- // check if lucky enough to have already loaded this file
- kp = cacheMap.get(resourceKey);
- }
-
- if (kp != null) {
- if (log.isTraceEnabled()) {
- PublicKey key = kp.getPublic();
- log.trace("doLoadKey({}) use cached key {}-{}",
- resourceKey, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
- }
- return kp;
- }
-
- kp = doLoadKey(resourceKey, resource, getPasswordFinder());
- if (kp != null) {
- boolean reusedKey;
- synchronized (cacheMap) {
- // if somebody else beat us to it, use the cached key - just in case file contents changed
- reusedKey = cacheMap.containsKey(resourceKey);
- if (reusedKey) {
- kp = cacheMap.get(resourceKey);
- } else {
- cacheMap.put(resourceKey, kp);
- }
- }
-
- if (log.isDebugEnabled()) {
- PublicKey key = kp.getPublic();
- log.debug("doLoadKey({}) {} {}-{}",
- resourceKey, reusedKey ? "re-loaded" : "loaded",
- KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
- }
- } else {
- if (log.isDebugEnabled()) {
- log.debug("doLoadKey({}) no key loaded", resourceKey);
- }
- }
-
- return kp;
- }
-
- protected KeyPair doLoadKey(String resourceKey, R resource, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
- try (InputStream inputStream = openKeyPairResource(resourceKey, resource)) {
- return doLoadKey(resourceKey, inputStream, provider);
- }
- }
-
- protected abstract InputStream openKeyPairResource(String resourceKey, R resource) throws IOException;
-
- protected KeyPair doLoadKey(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
- throws IOException, GeneralSecurityException {
- return SecurityUtils.loadKeyPairIdentity(resourceKey, inputStream, provider);
- }
-
- protected class KeyPairIterator implements Iterator<KeyPair> {
- private final Iterator<? extends R> iterator;
- private KeyPair nextKeyPair;
- private boolean nextKeyPairSet;
-
- protected KeyPairIterator(Collection<? extends R> resources) {
- iterator = resources.iterator();
- }
-
- @Override
- public boolean hasNext() {
- return nextKeyPairSet || setNextObject();
- }
-
- @Override
- public KeyPair next() {
- if (!nextKeyPairSet) {
- if (!setNextObject()) {
- throw new NoSuchElementException("Out of files to try");
- }
- }
- nextKeyPairSet = false;
- return nextKeyPair;
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException("loadKeys(files) Iterator#remove() N/A");
- }
-
- @SuppressWarnings("synthetic-access")
- private boolean setNextObject() {
- boolean debugEnabled = log.isDebugEnabled();
- while (iterator.hasNext()) {
- R r = iterator.next();
- try {
- nextKeyPair = doLoadKey(r);
- } catch (Throwable e) {
- log.warn("Failed (" + e.getClass().getSimpleName() + ")"
- + " to load key resource=" + r + ": " + e.getMessage());
- if (debugEnabled) {
- log.debug("Key resource=" + r + " load failure details", e);
- }
- nextKeyPair = null;
- continue;
- }
-
- if (nextKeyPair != null) {
- nextKeyPairSet = true;
- return true;
- }
- }
-
- return false;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java
deleted file mode 100644
index 6e8da54..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/ClassLoadableResourceKeyPairProvider.java
+++ /dev/null
@@ -1,113 +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.keyprovider;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.security.KeyPair;
-import java.util.Collection;
-import java.util.Collections;
-
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.threads.ThreadUtils;
-
-/**
- * This provider loads private keys from the specified resources that
- * are accessible via {@link ClassLoader#getResourceAsStream(String)}.
- * If no loader configured via {@link #setResourceLoader(ClassLoader)}, then
- * {@link ThreadUtils#resolveDefaultClassLoader(Class)} is used
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ClassLoadableResourceKeyPairProvider extends AbstractResourceKeyPairProvider<String> {
- private ClassLoader classLoader;
- private Collection<String> resources;
-
- public ClassLoadableResourceKeyPairProvider() {
- this(Collections.emptyList());
- }
-
- public ClassLoadableResourceKeyPairProvider(ClassLoader cl) {
- this(cl, Collections.emptyList());
- }
-
- public ClassLoadableResourceKeyPairProvider(String res) {
- this(Collections.singletonList(ValidateUtils.checkNotNullAndNotEmpty(res, "No resource specified")));
- }
-
- public ClassLoadableResourceKeyPairProvider(ClassLoader cl, String res) {
- this(cl, Collections.singletonList(ValidateUtils.checkNotNullAndNotEmpty(res, "No resource specified")));
- }
-
- public ClassLoadableResourceKeyPairProvider(Collection<String> resources) {
- this.classLoader = ThreadUtils.resolveDefaultClassLoader(getClass());
- this.resources = (resources == null) ? Collections.emptyList() : resources;
- }
-
- public ClassLoadableResourceKeyPairProvider(ClassLoader cl, Collection<String> resources) {
- this.classLoader = cl;
- this.resources = (resources == null) ? Collections.emptyList() : resources;
- }
-
- public Collection<String> getResources() {
- return resources;
- }
-
- public void setResources(Collection<String> resources) {
- this.resources = (resources == null) ? Collections.emptyList() : resources;
- }
-
- public ClassLoader getResourceLoader() {
- return classLoader;
- }
-
- public void setResourceLoader(ClassLoader classLoader) {
- this.classLoader = classLoader;
- }
-
- @Override
- public Iterable<KeyPair> loadKeys() {
- return loadKeys(getResources());
- }
-
- @Override
- protected InputStream openKeyPairResource(String resourceKey, String resource) throws IOException {
- ClassLoader cl = resolveClassLoader();
- if (cl == null) {
- throw new StreamCorruptedException("No resource loader for " + resource);
- }
-
- InputStream input = cl.getResourceAsStream(resource);
- if (input == null) {
- throw new FileNotFoundException("Cannot find resource " + resource);
- }
-
- return input;
- }
-
- protected ClassLoader resolveClassLoader() {
- ClassLoader cl = getResourceLoader();
- if (cl == null) {
- cl = ThreadUtils.resolveDefaultClassLoader(getClass());
- }
- return cl;
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
deleted file mode 100644
index c4aae97..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
+++ /dev/null
@@ -1,92 +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.keyprovider;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-
-/**
- * This host key provider loads private keys from the specified files. The
- * loading is <U>lazy</U> - i.e., a file is not loaded until it is actually
- * required. Once required though, its loaded {@link KeyPair} result is
- * <U>cached</U> and not re-loaded.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class FileKeyPairProvider extends AbstractResourceKeyPairProvider<Path> {
- private Collection<? extends Path> files;
-
- public FileKeyPairProvider() {
- super();
- }
-
- public FileKeyPairProvider(Path path) {
- this(Collections.singletonList(Objects.requireNonNull(path, "No path provided")));
- }
-
- public FileKeyPairProvider(Path... files) {
- this(Arrays.asList(files));
- }
-
- public FileKeyPairProvider(Collection<? extends Path> files) {
- this.files = files;
- }
-
- public Collection<? extends Path> getPaths() {
- return files;
- }
-
- public void setFiles(Collection<File> files) {
- setPaths(GenericUtils.map(files, File::toPath));
- }
-
- public void setPaths(Collection<? extends Path> paths) {
- // use absolute path in order to have unique cache keys
- Collection<Path> resolved = GenericUtils.map(paths, Path::toAbsolutePath);
- resetCacheMap(resolved);
- files = resolved;
- }
-
- @Override
- public Iterable<KeyPair> loadKeys() {
- return loadKeys(getPaths());
- }
-
- @Override
- protected KeyPair doLoadKey(Path resource) throws IOException, GeneralSecurityException {
- return super.doLoadKey((resource == null) ? null : resource.toAbsolutePath());
- }
-
- @Override
- protected InputStream openKeyPairResource(String resourceKey, Path resource) throws IOException {
- return Files.newInputStream(resource, IoUtils.EMPTY_OPEN_OPTIONS);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
deleted file mode 100644
index 2ef4fa2..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/KeyIdentityProvider.java
+++ /dev/null
@@ -1,204 +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.keyprovider;
-
-import java.security.KeyPair;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.function.Function;
-import java.util.function.Supplier;
-
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface KeyIdentityProvider {
- /**
- * An "empty" implementation of {@link KeyIdentityProvider} that
- * returns an empty group of key pairs
- */
- KeyIdentityProvider EMPTY_KEYS_PROVIDER = new KeyIdentityProvider() {
- @Override
- public Iterable<KeyPair> loadKeys() {
- return Collections.emptyList();
- }
-
- @Override
- public String toString() {
- return "EMPTY";
- }
- };
-
- /**
- * Invokes {@link KeyIdentityProvider#loadKeys()} and returns the result - ignores
- * {@code null} providers (i.e., returns an empty iterable instance)
- */
- Function<KeyIdentityProvider, Iterable<KeyPair>> LOADER = p ->
- (p == null) ? Collections.<KeyPair>emptyList() : p.loadKeys();
-
- /**
- * Load available keys.
- *
- * @return an {@link Iterable} instance of available keys - ignored if {@code null}
- */
- Iterable<KeyPair> loadKeys();
-
- /**
- * Creates a "unified" {@link KeyIdentityProvider} of key pairs out of the registered
- * {@link KeyPair} identities and the extra available ones as a single iterator
- * of key pairs
- *
- *
- * @param session The {@link ClientSession} - ignored if {@code null} (i.e., empty
- * iterator returned)
- * @return The wrapping KeyIdentityProvider
- * @see ClientSession#getRegisteredIdentities()
- * @see ClientSession#getKeyPairProvider()
- */
- static KeyIdentityProvider providerOf(ClientSession session) {
- return session == null
- ? EMPTY_KEYS_PROVIDER
- : resolveKeyIdentityProvider(session.getRegisteredIdentities(), session.getKeyPairProvider());
- }
-
- /**
- * Creates a "unified" {@link Iterator} of key pairs out of the registered
- * {@link KeyPair} identities and the extra available ones as a single iterator
- * of key pairs
- *
- * @param session The {@link ClientSession} - ignored if {@code null} (i.e., empty
- * iterator returned)
- * @return The wrapping iterator
- * @see ClientSession#getRegisteredIdentities()
- * @see ClientSession#getKeyPairProvider()
- */
- static Iterator<KeyPair> iteratorOf(ClientSession session) {
- return iteratorOf(providerOf(session));
- }
-
- /**
- * Creates a "unified" {@link Iterator} of {@link KeyPair}s out of 2 possible
- * {@link KeyIdentityProvider}
- *
- * @param identities The registered keys identities
- * @param keys Extra available key pairs
- * @return The wrapping iterator
- * @see #resolveKeyIdentityProvider(KeyIdentityProvider, KeyIdentityProvider)
- */
- static Iterator<KeyPair> iteratorOf(KeyIdentityProvider identities, KeyIdentityProvider keys) {
- return iteratorOf(resolveKeyIdentityProvider(identities, keys));
- }
-
- /**
- * Resolves a non-{@code null} iterator of the available keys
- *
- * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
- * @return A non-{@code null} iterator - which may be empty if no provider or no keys
- */
- static Iterator<KeyPair> iteratorOf(KeyIdentityProvider provider) {
- return GenericUtils.iteratorOf((provider == null) ? null : provider.loadKeys());
- }
-
- /**
- * <P>Creates a "unified" {@link KeyIdentityProvider} out of 2 possible ones
- * as follows:</P></BR>
- * <UL>
- * <LI>If both are {@code null} then return {@code null}.</LI>
- * <LI>If either one is {@code null} then use the non-{@code null} one.</LI>
- * <LI>If both are the same instance then use it.</U>
- * <LI>Otherwise, returns a wrapper that groups both providers.</LI>
- * </UL>
- * @param identities The registered key pair identities
- * @param keys The extra available key pairs
- * @return The resolved provider
- * @see #multiProvider(KeyIdentityProvider...)
- */
- static KeyIdentityProvider resolveKeyIdentityProvider(KeyIdentityProvider identities, KeyIdentityProvider keys) {
- if ((keys == null) || (identities == keys)) {
- return identities;
- } else if (identities == null) {
- return keys;
- } else {
- return multiProvider(identities, keys);
- }
- }
-
- /**
- * Wraps a group of {@link KeyIdentityProvider} into a single one
- *
- * @param providers The providers - ignored if {@code null}/empty (i.e., returns
- * {@link #EMPTY_KEYS_PROVIDER})
- * @return The wrapping provider
- * @see #multiProvider(Collection)
- */
- static KeyIdentityProvider multiProvider(KeyIdentityProvider... providers) {
- return multiProvider(GenericUtils.asList(providers));
- }
-
- /**
- * Wraps a group of {@link KeyIdentityProvider} into a single one
- *
- * @param providers The providers - ignored if {@code null}/empty (i.e., returns
- * {@link #EMPTY_KEYS_PROVIDER})
- * @return The wrapping provider
- */
- static KeyIdentityProvider multiProvider(Collection<? extends KeyIdentityProvider> providers) {
- return GenericUtils.isEmpty(providers) ? EMPTY_KEYS_PROVIDER : wrapKeyPairs(iterableOf(providers));
- }
-
- /**
- * Wraps a group of {@link KeyIdentityProvider} into an {@link Iterable} of {@link KeyPair}s
- *
- * @param providers The group of providers - ignored if {@code null}/empty (i.e., returns an
- * empty iterable instance)
- * @return The wrapping iterable
- */
- static Iterable<KeyPair> iterableOf(Collection<? extends KeyIdentityProvider> providers) {
- Iterable<Supplier<Iterable<KeyPair>>> keysSuppliers =
- GenericUtils.<KeyIdentityProvider, Supplier<Iterable<KeyPair>>>wrapIterable(providers, p -> p::loadKeys);
- return GenericUtils.multiIterableSuppliers(keysSuppliers);
- }
-
- /**
- * Wraps a group of {@link KeyPair}s into a {@link KeyIdentityProvider}
- *
- * @param pairs The key pairs - ignored if {@code null}/empty (i.e., returns
- * {@link #EMPTY_KEYS_PROVIDER}).
- * @return The provider wrapper
- */
- static KeyIdentityProvider wrapKeyPairs(KeyPair... pairs) {
- return wrapKeyPairs(GenericUtils.asList(pairs));
- }
-
- /**
- * Wraps a group of {@link KeyPair}s into a {@link KeyIdentityProvider}
- *
- * @param pairs The key pairs {@link Iterable} - ignored if {@code null} (i.e., returns
- * {@link #EMPTY_KEYS_PROVIDER}).
- * @return The provider wrapper
- */
- static KeyIdentityProvider wrapKeyPairs(Iterable<KeyPair> pairs) {
- return (pairs == null) ? EMPTY_KEYS_PROVIDER : () -> pairs;
- }
-}
[02/51] [abbrv] mina-sshd git commit: [SSHD-842] Split Putty key
files support code to separate artifact
Posted by lg...@apache.org.
[SSHD-842] Split Putty key files support code to separate artifact
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/f60fcb0a
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/f60fcb0a
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/f60fcb0a
Branch: refs/heads/master
Commit: f60fcb0a547441f001b2a624022af0f73b80ce7f
Parents: 10de190
Author: Goldstein Lyor <ly...@cb4.com>
Authored: Thu Sep 6 10:43:46 2018 +0300
Committer: Lyor Goldstein <ly...@gmail.com>
Committed: Thu Sep 6 19:07:54 2018 +0300
----------------------------------------------------------------------
README.md | 44 +++-
assembly/pom.xml | 5 +
pom.xml | 1 +
sshd-cli/pom.xml | 5 +
.../loader/putty/AbstractPuttyKeyDecoder.java | 218 -------------------
.../keys/loader/putty/DSSPuttyKeyDecoder.java | 65 ------
.../keys/loader/putty/ECDSAPuttyKeyDecoder.java | 98 ---------
.../keys/loader/putty/EdDSAPuttyKeyDecoder.java | 68 ------
.../putty/PuttyKeyPairResourceParser.java | 200 -----------------
.../keys/loader/putty/PuttyKeyReader.java | 75 -------
.../config/keys/loader/putty/PuttyKeyUtils.java | 67 ------
.../keys/loader/putty/RSAPuttyKeyDecoder.java | 72 ------
.../keys/loader/putty/PuttyKeyUtilsTest.java | 153 -------------
...KeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk | 10 -
...KeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk | 11 -
...KeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk | 12 -
.../putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk | 17 --
.../PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk | 9 -
.../putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk | 18 --
...-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk | 10 -
...-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk | 11 -
...-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk | 12 -
...t-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk | 17 --
...ssphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk | 9 -
...t-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk | 18 --
sshd-putty/pom.xml | 107 +++++++++
.../loader/putty/AbstractPuttyKeyDecoder.java | 218 +++++++++++++++++++
.../keys/loader/putty/DSSPuttyKeyDecoder.java | 65 ++++++
.../keys/loader/putty/ECDSAPuttyKeyDecoder.java | 98 +++++++++
.../keys/loader/putty/EdDSAPuttyKeyDecoder.java | 68 ++++++
.../putty/PuttyKeyPairResourceParser.java | 200 +++++++++++++++++
.../keys/loader/putty/PuttyKeyReader.java | 75 +++++++
.../config/keys/loader/putty/PuttyKeyUtils.java | 67 ++++++
.../keys/loader/putty/RSAPuttyKeyDecoder.java | 72 ++++++
sshd-putty/src/main/resources/.gitignore | 0
.../keys/loader/putty/PuttyKeyUtilsTest.java | 153 +++++++++++++
sshd-putty/src/test/resources/.gitignore | 0
sshd-putty/src/test/resources/log4j.properties | 38 ++++
...KeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk | 10 +
...KeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk | 11 +
...KeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk | 12 +
.../putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk | 17 ++
.../PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk | 9 +
.../putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk | 18 ++
...-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk | 10 +
...-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk | 11 +
...-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk | 12 +
...t-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk | 17 ++
...ssphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk | 9 +
...t-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk | 18 ++
50 files changed, 1366 insertions(+), 1174 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index cad3ea8..4fb0f17 100644
--- a/README.md
+++ b/README.md
@@ -1578,13 +1578,49 @@ The code contains [support for "wrapper" protocols](https://issues.apache.org/ji
* `SshServer/ServerSession#setServerProxyAcceptor` - sets a proxy that intercept the 1st incoming packet before being processed by the server
-## Useful extra components in _sshd-contrib_
+## Configuration/data files parsing support
+
+Most of the configuration data files parsing support resides in the _sshd-common_ artfiact:
+
+```xml
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>...same version as the rest of the artifacts...</version>
+ </dependency>
+```
+
+The code contains support for parsing the [_authorized_keys_](http://man.openbsd.org/sshd.8#AUTHORIZED_KEYS_FILE_FORMAT),
+[_known\_hosts_](http://www.manpagez.com/man/8/sshd/), [_ssh\_config_, _sshd\_config_](https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5),
+and [_~/config_](http://www.gsp.com/cgi-bin/man.cgi?topic=ssh_config) files. The code resides in the _sshd-common_ artifact - specifically
+the `KeyUtils#getPublicKeyEntryDecoder`, `AuthorizedKeyEntry#readAuthorizedKeys`, `KnownHostEntry#readKnownHostEntries`
+and `HostConfigEntry#readHostConfigEntries`.
+
+### PEM/OpenSSH
-* PUTTY key file(s) readers - see `org.apache.sshd.common.config.keys.loader.putty` package - specifically `PuttyKeyUtils#DEFAULT_INSTANCE KeyPairResourceParser`.
+The common code contains built-in support for parsing PEM and/or _OpenSSH_ formatted key files and using them for authentication purposes.
+As mentioned previously, it can leverage _Bouncycastle_ if available, but can do most of the work without it as well. For _ed25519_ support,
+one must provide the _eddsa_ artifact dependency.
+### [PUTTY](https://www.putty.org/)
+
+The code contains built-in support for parsing PUTTY key files (usually _.ppk_) and using them same as SSH ones as key-pair
+providers for autentication purposes. The PUTTY key file(s) readers are contained in the `org.apache.sshd.common.config.keys.loader.putty`
+package (specifically `PuttyKeyUtils#DEFAULT_INSTANCE KeyPairResourceParser`) of the _sshd-putty_ artifact. **Note:** the artifact should
+be included as an extra dependency:
+
+```xml
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-putty</artifactId>
+ <version>...same version as the rest of the artifacts...</version>
+ </dependency>
+```
+
+## Useful extra components in _sshd-contrib_
-* `InteractivePasswordIdentityProvider` - helps implement a `PasswordIdentityProvider` by delegating calls to `UserInteraction#getUpdatedPassword`.
-The way to use it would be as follows:
+* `InteractivePasswordIdentityProvider` - helps implement a `PasswordIdentityProvider` by delegating calls
+to `UserInteraction#getUpdatedPassword`. The way to use it would be as follows:
```java
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/assembly/pom.xml
----------------------------------------------------------------------
diff --git a/assembly/pom.xml b/assembly/pom.xml
index 93011a8..58f20a0 100644
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -43,6 +43,11 @@
</dependency>
<dependency>
<groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-putty</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
<version>${project.version}</version>
</dependency>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index a684d78..46d068e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1136,6 +1136,7 @@
<modules>
<module>sshd-common</module>
+ <module>sshd-putty</module>
<module>sshd-core</module>
<module>sshd-mina</module>
<module>sshd-netty</module>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-cli/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-cli/pom.xml b/sshd-cli/pom.xml
index 3babf6b..7b2c33a 100644
--- a/sshd-cli/pom.xml
+++ b/sshd-cli/pom.xml
@@ -52,6 +52,11 @@
<artifactId>sshd-sftp</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-putty</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<!-- Test dependencies -->
<dependency>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java
deleted file mode 100644
index d2428e2..0000000
--- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java
+++ /dev/null
@@ -1,218 +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.loader.putty;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Base64;
-import java.util.Base64.Decoder;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.impl.AbstractIdentityResourceLoader;
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @param <PUB> Generic public key type
- * @param <PRV> Generic private key type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends PrivateKey>
- extends AbstractIdentityResourceLoader<PUB, PRV>
- implements PuttyKeyPairResourceParser<PUB, PRV> {
- public static final String ENCRYPTION_HEADER = "Encryption";
-
- protected AbstractPuttyKeyDecoder(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
- super(pubType, prvType, names);
- }
-
- @Override
- public boolean canExtractKeyPairs(String resourceKey, List<String> lines)
- throws IOException, GeneralSecurityException {
- if (!PuttyKeyPairResourceParser.super.canExtractKeyPairs(resourceKey, lines)) {
- return false;
- }
-
- for (String l : lines) {
- l = GenericUtils.trimToEmpty(l);
- if (!l.startsWith(KEY_FILE_HEADER_PREFIX)) {
- continue;
- }
-
- int pos = l.indexOf(':');
- if ((pos <= 0) || (pos >= (l.length() - 1))) {
- return false;
- }
-
- Collection<String> supported = getSupportedTypeNames();
- String typeValue = l.substring(pos + 1).trim();
- return supported.contains(typeValue);
- }
-
- return false;
- }
-
- @Override
- public Collection<KeyPair> loadKeyPairs(
- String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
- throws IOException, GeneralSecurityException {
- List<String> pubLines = Collections.emptyList();
- List<String> prvLines = Collections.emptyList();
- String prvEncryption = null;
- for (int index = 0, numLines = lines.size(); index < numLines; index++) {
- String l = lines.get(index);
- l = GenericUtils.trimToEmpty(l);
- int pos = l.indexOf(':');
- if ((pos <= 0) || (pos >= (l.length() - 1))) {
- continue;
- }
-
- String hdrName = l.substring(0, pos).trim();
- String hdrValue = l.substring(pos + 1).trim();
- switch (hdrName) {
- case ENCRYPTION_HEADER:
- if (prvEncryption != null) {
- throw new StreamCorruptedException("Duplicate " + hdrName + " in" + resourceKey);
- }
- prvEncryption = hdrValue;
- break;
- case PUBLIC_LINES_HEADER:
- pubLines = extractDataLines(resourceKey, lines, index + 1, hdrName, hdrValue, pubLines);
- index += pubLines.size();
- break;
- case PRIVATE_LINES_HEADER:
- prvLines = extractDataLines(resourceKey, lines, index + 1, hdrName, hdrValue, prvLines);
- index += prvLines.size();
- break;
- default: // ignored
- }
- }
-
- return loadKeyPairs(resourceKey, pubLines, prvLines, prvEncryption, passwordProvider);
- }
-
- public static List<String> extractDataLines(
- String resourceKey, List<String> lines, int startIndex, String hdrName, String hdrValue, List<String> curLines)
- throws IOException {
- if (GenericUtils.size(curLines) > 0) {
- throw new StreamCorruptedException("Duplicate " + hdrName + " in " + resourceKey);
- }
-
- int numLines;
- try {
- numLines = Integer.parseInt(hdrValue);
- } catch (NumberFormatException e) {
- throw new StreamCorruptedException("Bad " + hdrName + " value (" + hdrValue + ") in " + resourceKey);
- }
-
- int endIndex = startIndex + numLines;
- int totalLines = lines.size();
- if (endIndex > totalLines) {
- throw new StreamCorruptedException("Excessive " + hdrName + " value (" + hdrValue + ") in " + resourceKey);
- }
-
- return lines.subList(startIndex, endIndex);
- }
-
- public Collection<KeyPair> loadKeyPairs(
- String resourceKey, List<String> pubLines, List<String> prvLines, String prvEncryption, FilePasswordProvider passwordProvider)
- throws IOException, GeneralSecurityException {
- return loadKeyPairs(resourceKey,
- KeyPairResourceParser.joinDataLines(pubLines), KeyPairResourceParser.joinDataLines(prvLines),
- prvEncryption, passwordProvider);
- }
-
- public Collection<KeyPair> loadKeyPairs(
- String resourceKey, String pubData, String prvData, String prvEncryption, FilePasswordProvider passwordProvider)
- throws IOException, GeneralSecurityException {
- Decoder b64Decoder = Base64.getDecoder();
- byte[] pubBytes = b64Decoder.decode(pubData);
- byte[] prvBytes = b64Decoder.decode(prvData);
- String password = null;
- if ((GenericUtils.length(prvEncryption) > 0)
- && (!NO_PRIVATE_KEY_ENCRYPTION_VALUE.equalsIgnoreCase(prvEncryption))) {
- password = passwordProvider.getPassword(resourceKey);
- }
-
- if (GenericUtils.isEmpty(prvEncryption)
- || NO_PRIVATE_KEY_ENCRYPTION_VALUE.equalsIgnoreCase(prvEncryption)
- || GenericUtils.isEmpty(password)) {
- return loadKeyPairs(resourceKey, pubBytes, prvBytes);
- }
-
- // format is "<cipher><bits>-<mode>" - e.g., "aes256-cbc"
- int pos = prvEncryption.indexOf('-');
- if (pos <= 0) {
- throw new StreamCorruptedException("Missing private key encryption mode in " + prvEncryption);
- }
-
- String mode = prvEncryption.substring(pos + 1).toUpperCase();
- String algName = null;
- int numBits = 0;
- for (int index = 0; index < pos; index++) {
- char ch = prvEncryption.charAt(index);
- if ((ch >= '0') && (ch <= '9')) {
- algName = prvEncryption.substring(0, index).toUpperCase();
- numBits = Integer.parseInt(prvEncryption.substring(index, pos));
- break;
- }
- }
-
- if (GenericUtils.isEmpty(algName) || (numBits <= 0)) {
- throw new StreamCorruptedException("Missing private key encryption algorithm details in " + prvEncryption);
- }
-
- prvBytes = PuttyKeyPairResourceParser.decodePrivateKeyBytes(prvBytes, algName, numBits, mode, password);
- return loadKeyPairs(resourceKey, pubBytes, prvBytes);
- }
-
- public Collection<KeyPair> loadKeyPairs(String resourceKey, byte[] pubData, byte[] prvData)
- throws IOException, GeneralSecurityException {
- ValidateUtils.checkNotNullAndNotEmpty(pubData, "No public key data in %s", resourceKey);
- ValidateUtils.checkNotNullAndNotEmpty(prvData, "No private key data in %s", resourceKey);
- try (InputStream pubStream = new ByteArrayInputStream(pubData);
- InputStream prvStream = new ByteArrayInputStream(prvData)) {
- return loadKeyPairs(resourceKey, pubStream, prvStream);
- }
- }
-
- public Collection<KeyPair> loadKeyPairs(String resourceKey, InputStream pubData, InputStream prvData)
- throws IOException, GeneralSecurityException {
- try (PuttyKeyReader pubReader =
- new PuttyKeyReader(ValidateUtils.checkNotNull(pubData, "No public key data in %s", resourceKey));
- PuttyKeyReader prvReader =
- new PuttyKeyReader(ValidateUtils.checkNotNull(prvData, "No private key data in %s", resourceKey))) {
- return loadKeyPairs(resourceKey, pubReader, prvReader);
- }
- }
-
- public abstract Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
- throws IOException, GeneralSecurityException;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java
deleted file mode 100644
index 366aead..0000000
--- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java
+++ /dev/null
@@ -1,65 +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.loader.putty;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-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.spec.DSAPrivateKeySpec;
-import java.security.spec.DSAPublicKeySpec;
-import java.util.Collection;
-import java.util.Collections;
-
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DSSPuttyKeyDecoder extends AbstractPuttyKeyDecoder<DSAPublicKey, DSAPrivateKey> {
- public static final DSSPuttyKeyDecoder INSTANCE = new DSSPuttyKeyDecoder();
-
- public DSSPuttyKeyDecoder() {
- super(DSAPublicKey.class, DSAPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_DSS));
- }
-
- @Override
- public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
- throws IOException, GeneralSecurityException {
- pubReader.skip(); // skip version
-
- BigInteger p = pubReader.readInt();
- BigInteger q = pubReader.readInt();
- BigInteger g = pubReader.readInt();
- BigInteger y = pubReader.readInt();
- BigInteger x = prvReader.readInt();
- KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
- PublicKey pubKey = kf.generatePublic(new DSAPublicKeySpec(y, p, q, g));
- PrivateKey prvKey = kf.generatePrivate(new DSAPrivateKeySpec(x, p, q, g));
- return Collections.singletonList(new KeyPair(pubKey, prvKey));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java
deleted file mode 100644
index a257ff8..0000000
--- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.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.keys.loader.putty;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPrivateKeySpec;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Collection;
-import java.util.Collections;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ECDSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<ECPublicKey, ECPrivateKey> {
- public static final ECDSAPuttyKeyDecoder INSTANCE = new ECDSAPuttyKeyDecoder();
-
- public ECDSAPuttyKeyDecoder() {
- super(ECPublicKey.class, ECPrivateKey.class, ECCurves.KEY_TYPES);
- }
-
- @Override
- public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
- throws IOException, GeneralSecurityException {
- if (!SecurityUtils.isECCSupported()) {
- throw new NoSuchAlgorithmException("ECC not supported for " + resourceKey);
- }
-
- String keyType = pubReader.readString();
- ECCurves curve = ECCurves.fromKeyType(keyType);
- if (curve == null) {
- throw new InvalidKeySpecException("Not an EC curve name: " + keyType);
- }
-
- String encCurveName = pubReader.readString();
- String keyCurveName = curve.getName();
- if (!keyCurveName.equals(encCurveName)) {
- throw new InvalidKeySpecException("Mismatched key curve name (" + keyCurveName + ") vs. encoded one (" + encCurveName + ")");
- }
-
- byte[] octets = pubReader.read();
- ECPoint w;
- try {
- w = ECCurves.octetStringToEcPoint(octets);
- if (w == null) {
- throw new InvalidKeySpecException("No public ECPoint generated for curve=" + keyCurveName
- + " from octets=" + BufferUtils.toHex(':', octets));
- }
- } catch (RuntimeException e) {
- throw new InvalidKeySpecException("Failed (" + e.getClass().getSimpleName() + ")"
- + " to generate public ECPoint for curve=" + keyCurveName
- + " from octets=" + BufferUtils.toHex(':', octets)
- + ": " + e.getMessage());
- }
-
- KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
- ECParameterSpec paramSpec = curve.getParameters();
- PublicKey pubKey = kf.generatePublic(new ECPublicKeySpec(w, paramSpec));
-
- BigInteger s = prvReader.readInt();
- PrivateKey prvKey = kf.generatePrivate(new ECPrivateKeySpec(s, paramSpec));
- return Collections.singletonList(new KeyPair(pubKey, prvKey));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java
deleted file mode 100644
index f5980ab..0000000
--- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java
+++ /dev/null
@@ -1,68 +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.loader.putty;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Collection;
-import java.util.Collections;
-
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderUtils;
-
-import net.i2p.crypto.eddsa.EdDSAPrivateKey;
-import net.i2p.crypto.eddsa.EdDSAPublicKey;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class EdDSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<EdDSAPublicKey, EdDSAPrivateKey> {
- public static final EdDSAPuttyKeyDecoder INSTANCE = new EdDSAPuttyKeyDecoder();
-
- public EdDSAPuttyKeyDecoder() {
- super(EdDSAPublicKey.class, EdDSAPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_ED25519));
- }
-
- @Override
- public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
- throws IOException, GeneralSecurityException {
- if (!SecurityUtils.isEDDSACurveSupported()) {
- throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " provider not supported for " + resourceKey);
- }
-
- String keyType = pubReader.readString();
- if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
- throw new InvalidKeySpecException("Not an " + SecurityUtils.EDDSA + " key: " + keyType);
- }
-
- byte[] seed = pubReader.read();
- PublicKey pubKey = EdDSASecurityProviderUtils.generateEDDSAPublicKey(seed);
- seed = prvReader.read();
- PrivateKey prvKey = EdDSASecurityProviderUtils.generateEDDSAPrivateKey(seed);
- return Collections.singletonList(new KeyPair(pubKey, prvKey));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java
deleted file mode 100644
index 06443d9..0000000
--- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java
+++ /dev/null
@@ -1,200 +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.loader.putty;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-import javax.crypto.Cipher;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.sshd.common.config.keys.IdentityResourceLoader;
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
-import org.apache.sshd.common.digest.BuiltinDigests;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-//CHECKSTYLE:OFF
-/**
- * Loads a {@link KeyPair} from PuTTY's ".ppk" file.
- * <P>Note(s):</P>
- * <UL>
- * <P><LI>
- * The file appears to be a text file but it doesn't have a fixed encoding like UTF-8.
- * We use UTF-8 as the default encoding - since the important part is all ASCII,
- * this shouldn't really hurt the interpretation of the key.
- * </LI></P>
- *
- * <P><LI>
- * Based on code from <A HREF="https://github.com/kohsuke/trilead-putty-extension">Kohsuke's Trilead Putty Extension</A>
- * </LI></P>
- *
- * <P><LI>
- * Encrypted keys requires AES-256-CBC support, which is available only if the
- * <A HREF="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html">
- * Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files</A> are installed
- * </LI></P>
- * </UL>
- *
- * <P>Sample PuTTY file format</P>
- * <PRE>
- * PuTTY-User-Key-File-2: ssh-rsa
- * Encryption: none
- * Comment: rsa-key-20080514
- * Public-Lines: 4
- * AAAAB3NzaC1yc2EAAAABJQAAAIEAiPVUpONjGeVrwgRPOqy3Ym6kF/f8bltnmjA2
- * BMdAtaOpiD8A2ooqtLS5zWYuc0xkW0ogoKvORN+RF4JI+uNUlkxWxnzJM9JLpnvA
- * HrMoVFaQ0cgDMIHtE1Ob1cGAhlNInPCRnGNJpBNcJ/OJye3yt7WqHP4SPCCLb6nL
- * nmBUrLM=
- * Private-Lines: 8
- * AAAAgGtYgJzpktzyFjBIkSAmgeVdozVhgKmF6WsDMUID9HKwtU8cn83h6h7ug8qA
- * hUWcvVxO201/vViTjWVz9ALph3uMnpJiuQaaNYIGztGJBRsBwmQW9738pUXcsUXZ
- * 79KJP01oHn6Wkrgk26DIOsz04QOBI6C8RumBO4+F1WdfueM9AAAAQQDmA4hcK8Bx
- * nVtEpcF310mKD3nsbJqARdw5NV9kCxPnEsmy7Sy1L4Ob/nTIrynbc3MA9HQVJkUz
- * 7V0va5Pjm/T7AAAAQQCYbnG0UEekwk0LG1Hkxh1OrKMxCw2KWMN8ac3L0LVBg/Tk
- * 8EnB2oT45GGeJaw7KzdoOMFZz0iXLsVLNUjNn2mpAAAAQQCN6SEfWqiNzyc/w5n/
- * lFVDHExfVUJp0wXv+kzZzylnw4fs00lC3k4PZDSsb+jYCMesnfJjhDgkUA0XPyo8
- * Emdk
- * Private-MAC: 50c45751d18d74c00fca395deb7b7695e3ed6f77
- * </PRE>
- * @param <PUB> Generic public key type
- * @param <PRV> Generic private key type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-//CHECKSTYLE:ON
-public interface PuttyKeyPairResourceParser<PUB extends PublicKey, PRV extends PrivateKey>
- extends IdentityResourceLoader<PUB, PRV>, KeyPairResourceParser {
- String KEY_FILE_HEADER_PREFIX = "PuTTY-User-Key-File";
- String PUBLIC_LINES_HEADER = "Public-Lines";
- String PRIVATE_LINES_HEADER = "Private-Lines";
- String PPK_FILE_SUFFIX = ".ppk";
-
- List<String> KNOWN_HEADERS =
- Collections.unmodifiableList(
- Arrays.asList(
- KEY_FILE_HEADER_PREFIX,
- PUBLIC_LINES_HEADER,
- PRIVATE_LINES_HEADER));
-
- /**
- * Value (case insensitive) used to denote that private key is not encrypted
- */
- String NO_PRIVATE_KEY_ENCRYPTION_VALUE = "none";
-
- @Override
- default boolean canExtractKeyPairs(String resourceKey, List<String> lines)
- throws IOException, GeneralSecurityException {
- if (GenericUtils.isEmpty(lines)) {
- return false;
- }
-
- for (String l : lines) {
- l = GenericUtils.trimToEmpty(l);
- for (String hdr : KNOWN_HEADERS) {
- if (l.startsWith(hdr)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- static byte[] decodePrivateKeyBytes(byte[] prvBytes, String algName, int numBits, String algMode, String password)
- throws GeneralSecurityException {
- Objects.requireNonNull(prvBytes, "No encrypted key bytes");
- ValidateUtils.checkNotNullAndNotEmpty(algName, "No encryption algorithm", GenericUtils.EMPTY_OBJECT_ARRAY);
- ValidateUtils.checkTrue(numBits > 0, "Invalid encryption key size: %d", numBits);
- ValidateUtils.checkNotNullAndNotEmpty(algMode, "No encryption mode", GenericUtils.EMPTY_OBJECT_ARRAY);
- ValidateUtils.checkNotNullAndNotEmpty(password, "No encryption password", GenericUtils.EMPTY_OBJECT_ARRAY);
-
- if (!"AES".equalsIgnoreCase(algName)) {
- throw new NoSuchAlgorithmException("decodePrivateKeyBytes(" + algName + "-" + numBits + "-" + algMode + ") N/A");
- }
-
- return decodePrivateKeyBytes(prvBytes, algName, algMode, numBits, new byte[16], toEncryptionKey(password));
- }
-
- static byte[] decodePrivateKeyBytes(
- byte[] encBytes, String cipherName, String cipherMode, int numBits, byte[] initVector, byte[] keyValue)
- throws GeneralSecurityException {
- String xform = cipherName + "/" + cipherMode + "/NoPadding";
- int maxAllowedBits = Cipher.getMaxAllowedKeyLength(xform);
- // see http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
- if (numBits > maxAllowedBits) {
- throw new InvalidKeySpecException("decodePrivateKeyBytes(" + xform + ")"
- + " required key length (" + numBits + ") exceeds max. available: " + maxAllowedBits);
- }
-
- SecretKeySpec skeySpec = new SecretKeySpec(keyValue, cipherName);
- IvParameterSpec ivspec = new IvParameterSpec(initVector);
- Cipher cipher = SecurityUtils.getCipher(xform);
- cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivspec);
-
- return cipher.doFinal(encBytes);
- }
-
- /**
- * Converts a pass-phrase into a key, by following the convention that PuTTY uses.
- * Used to decrypt the private key when it's encrypted.
- * @param passphrase the Password to be used as seed for the key - ignored
- * if {@code null}/empty
- * @return The encryption key bytes - {@code null} if no pass-phrase
- * @throws GeneralSecurityException If cannot retrieve SHA-1 digest
- * @see <A HREF="http://security.stackexchange.com/questions/71341/how-does-putty-derive-the-encryption-key-in-its-ppk-format">
- * How does Putty derive the encryption key in its .ppk format ?</A>
- */
- static byte[] toEncryptionKey(String passphrase) throws GeneralSecurityException {
- if (GenericUtils.isEmpty(passphrase)) {
- return null;
- }
-
- MessageDigest hash = SecurityUtils.getMessageDigest(BuiltinDigests.sha1.getAlgorithm());
- byte[] stateValue = {0, 0, 0, 0};
- byte[] passBytes = passphrase.getBytes(StandardCharsets.UTF_8);
- byte[] keyValue = new byte[32];
- for (int i = 0, remLen = keyValue.length; i < 2; i++) {
- hash.reset(); // just making sure
-
- stateValue[3] = (byte) i;
- hash.update(stateValue);
- hash.update(passBytes);
-
- byte[] digest = hash.digest();
- System.arraycopy(digest, 0, keyValue, i * 20, Math.min(20, remLen));
- remLen -= 20;
- }
-
- return keyValue;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java
deleted file mode 100644
index 4fb63d1..0000000
--- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java
+++ /dev/null
@@ -1,75 +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.loader.putty;
-
-import java.io.Closeable;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-
-/**
- * Helper class for {@code Putty} key files decoders
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class PuttyKeyReader implements Closeable {
- private final DataInputStream di;
-
- public PuttyKeyReader(InputStream s) {
- di = new DataInputStream(s);
- }
-
- public void skip() throws IOException {
- int skipSize = di.readInt();
- int effectiveSkip = di.skipBytes(skipSize);
- if (skipSize != effectiveSkip) {
- throw new StreamCorruptedException("Mismatched skip size: expected" + skipSize + ", actual=" + effectiveSkip);
- }
- }
-
- public String readString() throws IOException {
- return readString(StandardCharsets.UTF_8);
- }
-
- public String readString(Charset cs) throws IOException {
- byte[] data = read();
- return new String(data, cs);
- }
-
- public BigInteger readInt() throws IOException {
- byte[] bytes = read();
- return new BigInteger(bytes);
- }
-
- public byte[] read() throws IOException {
- int len = di.readInt();
- byte[] r = new byte[len];
- di.readFully(r);
- return r;
- }
-
- @Override
- public void close() throws IOException {
- di.close();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java
deleted file mode 100644
index e750ace..0000000
--- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java
+++ /dev/null
@@ -1,67 +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.loader.putty;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.NavigableMap;
-import java.util.TreeMap;
-
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class PuttyKeyUtils {
- public static final List<PuttyKeyPairResourceParser<?, ?>> DEFAULT_PARSERS;
-
- public static final NavigableMap<String, PuttyKeyPairResourceParser<?, ?>> BY_KEY_TYPE;
-
- public static final KeyPairResourceParser DEFAULT_INSTANCE;
-
- static {
- List<PuttyKeyPairResourceParser<?, ?>> parsers = new ArrayList<>();
- parsers.add(RSAPuttyKeyDecoder.INSTANCE);
- parsers.add(DSSPuttyKeyDecoder.INSTANCE);
- if (SecurityUtils.isECCSupported()) {
- parsers.add(ECDSAPuttyKeyDecoder.INSTANCE);
- }
- if (SecurityUtils.isEDDSACurveSupported()) {
- parsers.add(EdDSAPuttyKeyDecoder.INSTANCE);
- }
- NavigableMap<String, PuttyKeyPairResourceParser<?, ?>> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- for (PuttyKeyPairResourceParser<?, ?> p : parsers) {
- Collection<String> supported = p.getSupportedTypeNames();
- for (String k : supported) {
- map.put(k, p);
- }
- }
- DEFAULT_PARSERS = Collections.unmodifiableList(parsers);
- BY_KEY_TYPE = Collections.unmodifiableNavigableMap(map);
- DEFAULT_INSTANCE = KeyPairResourceParser.aggregate(parsers);
- }
-
- private PuttyKeyUtils() {
- throw new UnsupportedOperationException("No instance");
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java
deleted file mode 100644
index 0a55d55..0000000
--- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java
+++ /dev/null
@@ -1,72 +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.loader.putty;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.RSAPrivateCrtKeySpec;
-import java.security.spec.RSAPrivateKeySpec;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.Collection;
-import java.util.Collections;
-
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class RSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<RSAPublicKey, RSAPrivateKey> {
- public static final RSAPuttyKeyDecoder INSTANCE = new RSAPuttyKeyDecoder();
-
- public RSAPuttyKeyDecoder() {
- super(RSAPublicKey.class, RSAPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_RSA));
- }
-
- @Override
- public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
- throws IOException, GeneralSecurityException {
- pubReader.skip(); // skip version
-
- KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
- BigInteger publicExp = pubReader.readInt();
- BigInteger modulus = pubReader.readInt();
- PublicKey pubKey = kf.generatePublic(new RSAPublicKeySpec(modulus, publicExp));
-
- BigInteger privateExp = prvReader.readInt();
- BigInteger primeP = prvReader.readInt();
- BigInteger primeQ = prvReader.readInt();
- BigInteger crtCoef = prvReader.readInt();
- BigInteger primeExponentP = privateExp.mod(primeP.subtract(BigInteger.ONE));
- BigInteger primeExponentQ = privateExp.mod(primeQ.subtract(BigInteger.ONE));
- RSAPrivateKeySpec prvSpec = new RSAPrivateCrtKeySpec(
- modulus, publicExp, privateExp, primeP, primeQ, primeExponentP, primeExponentQ, crtCoef);
- PrivateKey prvKey = kf.generatePrivate(prvSpec);
- return Collections.singletonList(new KeyPair(pubKey, prvKey));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java b/sshd-contrib/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java
deleted file mode 100644
index 8b70f77..0000000
--- a/sshd-contrib/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java
+++ /dev/null
@@ -1,153 +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.loader.putty;
-
-import java.io.IOException;
-import java.net.URL;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.sshd.common.cipher.BuiltinCiphers;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.loader.openssh.OpenSSHKeyPairResourceParser;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.Assume;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class PuttyKeyUtilsTest extends BaseTestSupport {
- public static final String PASSWORD = "super secret passphrase";
-
- private final String keyType;
- private final String regularFile;
- private final String encryptedFile;
- private final PuttyKeyPairResourceParser<?, ?> parser;
-
- public PuttyKeyUtilsTest(String keyType) {
- this.keyType = keyType;
- this.parser = PuttyKeyUtils.BY_KEY_TYPE.get(keyType);
- this.regularFile = getClass().getSimpleName()
- + "-" + keyType + "-" + KeyPair.class.getSimpleName()
- + PuttyKeyPairResourceParser.PPK_FILE_SUFFIX;
- this.encryptedFile = PASSWORD.replace(' ', '-') + "-AES-256-CBC"
- + "-" + keyType + "-" + KeyPair.class.getSimpleName()
- + PuttyKeyPairResourceParser.PPK_FILE_SUFFIX;
- }
-
- @Parameters(name = "{0}")
- public static List<Object[]> parameters() {
- return parameterize(PuttyKeyUtils.BY_KEY_TYPE.keySet());
- }
-
- @Test
- public void testCanDecodePuttyKeyFile() throws IOException, GeneralSecurityException {
- for (String resource : new String[]{regularFile, encryptedFile}) {
- URL url = getClass().getResource(resource);
- if (GenericUtils.isSameReference(regularFile, resource)) {
- assertNotNull("Missing test resource: " + resource, url);
- } else {
- if (url == null) {
- outputDebugMessage("Skip non-existing encrypted file: %s", resource);
- continue;
- }
- }
-
- List<String> lines = IoUtils.readAllLines(url);
- assertTrue(resource + " - can extract key pair", parser.canExtractKeyPairs(resource, lines));
-
- for (PuttyKeyPairResourceParser<?, ?> other : PuttyKeyUtils.BY_KEY_TYPE.values()) {
- if (parser == other) {
- continue;
- }
-
- assertFalse(other.getClass().getSimpleName() + "/" + resource + " - unexpected extraction capability",
- other.canExtractKeyPairs(resource, lines));
- }
- }
- }
-
- @Test
- public void testDecodePuttyKeyFile() throws IOException, GeneralSecurityException {
- URL url = getClass().getResource(regularFile);
- assertNotNull("Missing test resource: " + regularFile, url);
-
- Collection<KeyPair> keys = parser.loadKeyPairs(url, null);
- assertEquals("Mismatched loaded keys count from " + regularFile, 1, GenericUtils.size(keys));
- assertLoadedKeyPair(regularFile, keys.iterator().next());
- }
-
- @Test
- public void testDecodeEncryptedPuttyKeyFile() throws IOException, GeneralSecurityException {
- Assume.assumeTrue(BuiltinCiphers.aes256cbc.getTransformation() + " N/A", BuiltinCiphers.aes256cbc.isSupported());
- URL url = getClass().getResource(encryptedFile);
- Assume.assumeTrue("Skip non-existent encrypted file: " + encryptedFile, url != null);
- assertNotNull("Missing test resource: " + encryptedFile, url);
-
- Collection<KeyPair> keys = parser.loadKeyPairs(url, r -> PASSWORD);
- assertEquals("Mismatched loaded keys count from " + encryptedFile, 1, GenericUtils.size(keys));
-
- assertLoadedKeyPair(encryptedFile, keys.iterator().next());
- }
-
- private void assertLoadedKeyPair(String prefix, KeyPair kp) throws GeneralSecurityException {
- assertNotNull(prefix + ": no key pair loaded", kp);
-
- PublicKey pubKey = kp.getPublic();
- assertNotNull(prefix + ": no public key loaded", pubKey);
- assertEquals(prefix + ": mismatched public key type", keyType, KeyUtils.getKeyType(pubKey));
-
- PrivateKey prvKey = kp.getPrivate();
- assertNotNull(prefix + ": no private key loaded", prvKey);
- assertEquals(prefix + ": mismatched private key type", keyType, KeyUtils.getKeyType(prvKey));
-
- @SuppressWarnings("rawtypes")
- PrivateKeyEntryDecoder decoder =
- OpenSSHKeyPairResourceParser.getPrivateKeyEntryDecoder(prvKey);
- assertNotNull("No private key decoder", decoder);
-
- if (decoder.isPublicKeyRecoverySupported()) {
- @SuppressWarnings("unchecked")
- PublicKey recKey = decoder.recoverPublicKey(prvKey);
- assertKeyEquals("Mismatched recovered public key", pubKey, recKey);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk
deleted file mode 100644
index 509538a..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk
+++ /dev/null
@@ -1,10 +0,0 @@
-PuTTY-User-Key-File-2: ecdsa-sha2-nistp256
-Encryption: none
-Comment: ecdsa-key-20170917
-Public-Lines: 3
-AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM99zj2+E6AN
-xMZ/2SKFP/fAvPfUJUdsgJyn4g7nf36vNpoaRyq1FyHLxyT34AgTl1n3DwcaBXXC
-O5pCv6xFwYk=
-Private-Lines: 1
-AAAAIQDdmu/Dr68r6a0PNbQUN1bX+zS6J5jFsOlEAx8sR73Tuw==
-Private-MAC: 012e0d61593a431ae84beb6216dd29e4b203c1c0
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk
deleted file mode 100644
index 238261c..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk
+++ /dev/null
@@ -1,11 +0,0 @@
-PuTTY-User-Key-File-2: ecdsa-sha2-nistp384
-Encryption: none
-Comment: ecdsa-key-20170917
-Public-Lines: 3
-AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNHjIMrdMfXw
-CUqBAhkZw0vXB+qypkiTcL1CmcopmPrKvGHFieFmedeCQotjwJkoAAeb5isZNOXy
-h+7TnHGNrE/pZkHuNwACilpOt659hbhR2OGHX0jdpb8y4RVkuPQssg==
-Private-Lines: 2
-AAAAMHNGt3UPG3evJVl1GRoXXnqTafWLDQdWA2A1tXi8oRW0hhHMRe9/v0SCGL7S
-nL3asg==
-Private-MAC: 6fb6e93559ecacfa468aa5ff9776e4d4283db5ba
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk
deleted file mode 100644
index f60a78e..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk
+++ /dev/null
@@ -1,12 +0,0 @@
-PuTTY-User-Key-File-2: ecdsa-sha2-nistp521
-Encryption: none
-Comment: ecdsa-key-20170917
-Public-Lines: 4
-AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADGM237T9rT
-zE++sOFDN0VWfYfojlQ8dYP82OlgA24Yh0ZpOsezBBiHtHfMHl9tWHmch1YKmH7B
-lOfqbOgcIhz9cwA2V7Nu3IUGqxZT18LOXEpcdyDSphJ6jsy1urqBLrOz4DF6Udyr
-rFV4OQELovf8YdUsM91YPfe1DfnSRi1I5v20uA==
-Private-Lines: 2
-AAAAQgE28iZoHARx+2VzL7Y45FaY44TngX2b4StlC8wOlYF7NY/ba+Pt2RT/WcNL
-ytmLdj5QeV/fFJ9x8i00mTU6KCF2AA==
-Private-MAC: 7379e9986066087dff9339d2b0b968c2b31f45c7
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk
deleted file mode 100644
index 59a8fac..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk
+++ /dev/null
@@ -1,17 +0,0 @@
-PuTTY-User-Key-File-2: ssh-dss
-Encryption: none
-Comment: dsa-key-20130709
-Public-Lines: 10
-AAAAB3NzaC1kc3MAAACBAMg/IxsG5BxnF5gM7IKqqR0rftxZC+n5GlbO+J4H+iIb
-/KR8NBehkxG3CrBZMF96M2K1sEGYLob+3k4r71oWaPul8n5rt9kpd+JSq4iD2ygO
-yg6Kd1/YDBHoxneizy6I/bGsLwhAAKWcRNrXmYVKGzhrhvZWN12AJDq2mGdj3szL
-AAAAFQD7a2MltdUSF7FU3//SpW4WGjZbeQAAAIBf0nNsfKQL/TEMo7IpTrEMg5V0
-RnSigCX0+yUERS42GW/ZeCZBJw7oL2XZbuBtu63vMjDgVpnb92BdrcPgjJ7EFW6D
-lcyeuywStmg1ygXmDR2AQCxv0eX2CQgrdUczmRa155SDVUTvTQlO1IyKx0vwKAh1
-H7E3yJUfkTAJstbGYQAAAIEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJV
-c64kFs4yx+abYpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4
-dVbQZsdt+SLaXWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3
-T7A=
-Private-Lines: 1
-AAAAFQCE6flGnmVCAbzo9YsbdJWBnxMnBA==
-Private-MAC: 6ace0a5e5bf23649c1375e91dcd71d1def6c6aa1
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk
deleted file mode 100644
index 614ac69..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk
+++ /dev/null
@@ -1,9 +0,0 @@
-PuTTY-User-Key-File-2: ssh-ed25519
-Encryption: none
-Comment: ed25519-key-20170917
-Public-Lines: 2
-AAAAC3NzaC1lZDI1NTE5AAAAIN7fuKSIM5TbAX/1I1Ts3tfyo5eEs7JpmKsegHs/
-9fIi
-Private-Lines: 1
-AAAAIADKJJPxsUp7JXLzm1zwk8UswW/lkiwPJ73CbqGvalgP
-Private-MAC: 28a22234152feaf1d9a6a10ca0ae3a51b5e6dd52
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk
deleted file mode 100644
index da2868e..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk
+++ /dev/null
@@ -1,18 +0,0 @@
-PuTTY-User-Key-File-2: ssh-rsa
-Encryption: none
-Comment: rsa-key-20130709
-Public-Lines: 4
-AAAAB3NzaC1yc2EAAAABJQAAAIBLSj7+SllQdWjkD8EN4sA/fUv/jhc+ssGmCYx3
-uoiPMxjKH3xUPWu4zxJzhdlgFy4gchzjU51fDS4Oel6xGoWbGKGe4ZLQdE/t8N8l
-jAfOm/5lGp5tFhHs9UHoSm/h3RsErWNjKPjTGIlID35IcOXVhfhp9fX0RU6y/ZBI
-PhM20w==
-Private-Lines: 8
-AAAAgBx89T2fl2qNSkh0qemUEWQhkmCyTfwMSUW+bIBUawXATpGrDHLmztBOWgIy
-pUb0A52S9iyAgLwugCEnYhl/qCxvoARH7ZyTdYAL4KjJDySxVuqeo/ZhLscYcMAz
-MOyn8g5cR4dRgEwJ1/pRuK8r4+Z96zJG4NlxlHsUjHuj7t1dAAAAQQCTrj48XKIX
-M3dxWLSsSXbUCOpmAOTviuz9LD0H1ik7a6ebr0P6GTl9z7iscBgzdjBIHMFcdvar
-ophUJ5iRanCvAAAAQQCCg1VU1H5FHMipRvvw8b/zRqDsx6GTZs03ffhyKrTl1Dcd
-0oKy5/U3kQwdXPOSlVZeyX8nUVE2o7DOh7INsX0dAAAAQHkPjxivrN0SQuVAx+tK
-uRJ8vGL7sBOZ9gdTS25T3BVEkhRt37aDcshrodzDCzd515cwhmbLSsOsgyxcTwcX
-7SA=
-Private-MAC: 2416438f1a7ebdd33d519f6102d843b5f2c565d4
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk
deleted file mode 100644
index 4c601c7..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk
+++ /dev/null
@@ -1,10 +0,0 @@
-PuTTY-User-Key-File-2: ecdsa-sha2-nistp256
-Encryption: aes256-cbc
-Comment: ecdsa-key-20170917
-Public-Lines: 3
-AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM99zj2+E6AN
-xMZ/2SKFP/fAvPfUJUdsgJyn4g7nf36vNpoaRyq1FyHLxyT34AgTl1n3DwcaBXXC
-O5pCv6xFwYk=
-Private-Lines: 1
-/8MdniIqAaST5t3/bRx4mFdFxqN8jIwI0Hh7VTy8IV143j+PktgGIoHsUwTiEujQ
-Private-MAC: be4b37d4b65ad09e6890534a2ba355599da796c6
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk
deleted file mode 100644
index f57f5fd..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk
+++ /dev/null
@@ -1,11 +0,0 @@
-PuTTY-User-Key-File-2: ecdsa-sha2-nistp384
-Encryption: aes256-cbc
-Comment: ecdsa-key-20170917
-Public-Lines: 3
-AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNHjIMrdMfXw
-CUqBAhkZw0vXB+qypkiTcL1CmcopmPrKvGHFieFmedeCQotjwJkoAAeb5isZNOXy
-h+7TnHGNrE/pZkHuNwACilpOt659hbhR2OGHX0jdpb8y4RVkuPQssg==
-Private-Lines: 2
-MOgJnSZ8ZqGHFVtQvYKpOyTGWjMVIjIOMIUwhbxBuTiQ7WEyNPn9jjTsSwXtJxrG
-UI6NGeIqZ41P2e8JINhMIg==
-Private-MAC: 5ec40946270150ddfca35cce61f4265d7bfe7b7f
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk
deleted file mode 100644
index 97077fd..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk
+++ /dev/null
@@ -1,12 +0,0 @@
-PuTTY-User-Key-File-2: ecdsa-sha2-nistp521
-Encryption: aes256-cbc
-Comment: ecdsa-key-20170917
-Public-Lines: 4
-AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADGM237T9rT
-zE++sOFDN0VWfYfojlQ8dYP82OlgA24Yh0ZpOsezBBiHtHfMHl9tWHmch1YKmH7B
-lOfqbOgcIhz9cwA2V7Nu3IUGqxZT18LOXEpcdyDSphJ6jsy1urqBLrOz4DF6Udyr
-rFV4OQELovf8YdUsM91YPfe1DfnSRi1I5v20uA==
-Private-Lines: 2
-+44AQO4aflPquBZbdB3UtdLuXuHV2u1YoghxYXPFGdhskMt+XjJhUlrOHNX8rmgR
-E9nni474zGw9ni/3LIZwMILQI/xTfiQm3t6nKFV8nyI=
-Private-MAC: 99c25e348a84de4876163758ad13b2ad1dc43629
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk
deleted file mode 100644
index 9da31f1..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk
+++ /dev/null
@@ -1,17 +0,0 @@
-PuTTY-User-Key-File-2: ssh-dss
-Encryption: aes256-cbc
-Comment: dsa-key-20130909
-Public-Lines: 10
-AAAAB3NzaC1kc3MAAACBALVdprWfZ7+FiITCILnDkeMZ2ntkV2WjW5RcyiQvJvBO
-jCNiVtK87xATEOfBb20YvNZ/CibBjGS1TL5TBqRV5XleucPHMJZ5rXdJ2FH5oZnL
-kna3Et+L1/O/GQMmp2vfSFrO3n3+mI1Jozx3FoQO8jr1zIerJ5Mc4LKqsIQB9hvR
-AAAAFQD+z1y1/4ll4ax3rri8mkYgGDhqIQAAAIBVU4VJ7V7GoEQJ5WBMbpDEcLIZ
-KUgSHsJMQzWnLOi/DcsPjVMDX6FWGPLtrjd7fgInlPMCC/SPAhXdaXMvHZSkvBHV
-DfNjpsDgsxBnK1FKqRGtD49rETFGDl92EOsyBhv+9ymdOPX6R0hCqS+ulZheQPXI
-iHXdIvQK2Ev5Dy3xNgAAAIA8qhumHZcKss+Puuw+mY5J5Qt7Omv16MuDsYiVqrBq
-1V2C9gutx3Tu+n5QYi0xPlkkP/knMtkUZS+Wt3Dr8zPcEzNBc/Tm2EdYp11jZNx4
-4PM4ing+aCU5oGcg/7RS5CrY5Fn/rvgHqK22XiC8/U55iti44bWKvI6HCejExeZX
-iA==
-Private-Lines: 1
-64IdcIX48CNGjhcxsVN0vSNpT7S72e3FVdQ3t4ENAvI=
-Private-MAC: 8e62b44b5c080965e361936520be4feaa50285b1
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk
deleted file mode 100644
index 668ef1e..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk
+++ /dev/null
@@ -1,9 +0,0 @@
-PuTTY-User-Key-File-2: ssh-ed25519
-Encryption: aes256-cbc
-Comment: ed25519-key-20170917
-Public-Lines: 2
-AAAAC3NzaC1lZDI1NTE5AAAAIN7fuKSIM5TbAX/1I1Ts3tfyo5eEs7JpmKsegHs/
-9fIi
-Private-Lines: 1
-0cPG5BR80jQcJmHKs6IjpHS3R4/CTnudnJB4BcjaqKlRk0l603GVMDzTxkaICCb8
-Private-MAC: 381cff136b2516331ff4511cf382533fc14f0aeb
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk
deleted file mode 100644
index 2a11d04..0000000
--- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk
+++ /dev/null
@@ -1,18 +0,0 @@
-PuTTY-User-Key-File-2: ssh-rsa
-Encryption: aes256-cbc
-Comment: rsa-key-20130909
-Public-Lines: 4
-AAAAB3NzaC1yc2EAAAABJQAAAIEAhY5eZJrX0a3+rtEZCq0nu2zvAHp16nk93jhi
-p7c9tTDlGm9QEAgqzmuilEeUQ4BssxAvhCFEo/7Qbg4M7PwcA5cFkjXE4gj0YDJM
-ay7l2mb5aIoS/hACgNz54p/w/UgfQC1Vygt6QtvXXAW8Lh/YCN4Zw4ViROUhoYuy
-3K5SBYs=
-Private-Lines: 8
-mqcGPnrv9d1tYkJZSGaCy5REslPZ2xh8m7qAbN+bD1m7iQ77pLxlKyzs82rbRaC9
-KSnKwsbFl7o92NT+9yYKJ7ehXyWyrUXkn9KcPk7MzNVwMuWVDXwvHodGLCyVCLYq
-PNipvg2USHvnCjnnvtMysBRNJiHTMOaf/gSZLyaEuznYo3FEClMPzggY9b2nrxnV
-O1ttk1FJatkRflwFjn3A/R/GpowmBnkDyCkVlTvR+uBAg8iIy1Vzj5zIV9zmzfgx
-DxPot+Y81y+Xe3ohVh2s1FVvLw+KQbYbCQam5j0V/dTQ+oVWjCJBlibD3aVTGK0M
-Jswz8wPwXFo5N0yX/6ZTrshbvTzoO1bg0+HUu581ZSAeqttk9C1RLmWFS8YDm0Hn
-GhDXrjuAvKJ3cjeVJsumgVw45NYGARuzV24TlHUtU+eze8Y/0NsPJXoCfVoYjTjb
-fjlMh9rYbRdyNHXwYTzwbw==
-Private-MAC: f4c50b3da0b73c34e8989411fc48c884c09e20a0
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-putty/pom.xml b/sshd-putty/pom.xml
new file mode 100644
index 0000000..8e11385
--- /dev/null
+++ b/sshd-putty/pom.xml
@@ -0,0 +1,107 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd</artifactId>
+ <version>2.0.1-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+
+ <artifactId>sshd-putty</artifactId>
+ <name>Apache Mina SSHD :: Putty key files support utilities</name>
+ <packaging>jar</packaging>
+ <inceptionYear>2018</inceptionYear>
+
+ <properties>
+ <projectRoot>${project.basedir}/..</projectRoot>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpg-jdk15on</artifactId>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ <optional>true</optional>
+ </dependency>
+
+ <!-- For ed25519 support -->
+ <dependency>
+ <groupId>net.i2p.crypto</groupId>
+ <artifactId>eddsa</artifactId>
+ <optional>true</optional>
+ </dependency>
+
+ <!-- test dependencies -->
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <redirectTestOutputToFile>true</redirectTestOutputToFile>
+ <reportsDirectory>${project.build.directory}/surefire-reports-putty</reportsDirectory>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java
new file mode 100644
index 0000000..d2428e2
--- /dev/null
+++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java
@@ -0,0 +1,218 @@
+/*
+ * 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.loader.putty;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Base64;
+import java.util.Base64.Decoder;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.impl.AbstractIdentityResourceLoader;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * @param <PUB> Generic public key type
+ * @param <PRV> Generic private key type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends PrivateKey>
+ extends AbstractIdentityResourceLoader<PUB, PRV>
+ implements PuttyKeyPairResourceParser<PUB, PRV> {
+ public static final String ENCRYPTION_HEADER = "Encryption";
+
+ protected AbstractPuttyKeyDecoder(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
+ super(pubType, prvType, names);
+ }
+
+ @Override
+ public boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+ throws IOException, GeneralSecurityException {
+ if (!PuttyKeyPairResourceParser.super.canExtractKeyPairs(resourceKey, lines)) {
+ return false;
+ }
+
+ for (String l : lines) {
+ l = GenericUtils.trimToEmpty(l);
+ if (!l.startsWith(KEY_FILE_HEADER_PREFIX)) {
+ continue;
+ }
+
+ int pos = l.indexOf(':');
+ if ((pos <= 0) || (pos >= (l.length() - 1))) {
+ return false;
+ }
+
+ Collection<String> supported = getSupportedTypeNames();
+ String typeValue = l.substring(pos + 1).trim();
+ return supported.contains(typeValue);
+ }
+
+ return false;
+ }
+
+ @Override
+ public Collection<KeyPair> loadKeyPairs(
+ String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+ throws IOException, GeneralSecurityException {
+ List<String> pubLines = Collections.emptyList();
+ List<String> prvLines = Collections.emptyList();
+ String prvEncryption = null;
+ for (int index = 0, numLines = lines.size(); index < numLines; index++) {
+ String l = lines.get(index);
+ l = GenericUtils.trimToEmpty(l);
+ int pos = l.indexOf(':');
+ if ((pos <= 0) || (pos >= (l.length() - 1))) {
+ continue;
+ }
+
+ String hdrName = l.substring(0, pos).trim();
+ String hdrValue = l.substring(pos + 1).trim();
+ switch (hdrName) {
+ case ENCRYPTION_HEADER:
+ if (prvEncryption != null) {
+ throw new StreamCorruptedException("Duplicate " + hdrName + " in" + resourceKey);
+ }
+ prvEncryption = hdrValue;
+ break;
+ case PUBLIC_LINES_HEADER:
+ pubLines = extractDataLines(resourceKey, lines, index + 1, hdrName, hdrValue, pubLines);
+ index += pubLines.size();
+ break;
+ case PRIVATE_LINES_HEADER:
+ prvLines = extractDataLines(resourceKey, lines, index + 1, hdrName, hdrValue, prvLines);
+ index += prvLines.size();
+ break;
+ default: // ignored
+ }
+ }
+
+ return loadKeyPairs(resourceKey, pubLines, prvLines, prvEncryption, passwordProvider);
+ }
+
+ public static List<String> extractDataLines(
+ String resourceKey, List<String> lines, int startIndex, String hdrName, String hdrValue, List<String> curLines)
+ throws IOException {
+ if (GenericUtils.size(curLines) > 0) {
+ throw new StreamCorruptedException("Duplicate " + hdrName + " in " + resourceKey);
+ }
+
+ int numLines;
+ try {
+ numLines = Integer.parseInt(hdrValue);
+ } catch (NumberFormatException e) {
+ throw new StreamCorruptedException("Bad " + hdrName + " value (" + hdrValue + ") in " + resourceKey);
+ }
+
+ int endIndex = startIndex + numLines;
+ int totalLines = lines.size();
+ if (endIndex > totalLines) {
+ throw new StreamCorruptedException("Excessive " + hdrName + " value (" + hdrValue + ") in " + resourceKey);
+ }
+
+ return lines.subList(startIndex, endIndex);
+ }
+
+ public Collection<KeyPair> loadKeyPairs(
+ String resourceKey, List<String> pubLines, List<String> prvLines, String prvEncryption, FilePasswordProvider passwordProvider)
+ throws IOException, GeneralSecurityException {
+ return loadKeyPairs(resourceKey,
+ KeyPairResourceParser.joinDataLines(pubLines), KeyPairResourceParser.joinDataLines(prvLines),
+ prvEncryption, passwordProvider);
+ }
+
+ public Collection<KeyPair> loadKeyPairs(
+ String resourceKey, String pubData, String prvData, String prvEncryption, FilePasswordProvider passwordProvider)
+ throws IOException, GeneralSecurityException {
+ Decoder b64Decoder = Base64.getDecoder();
+ byte[] pubBytes = b64Decoder.decode(pubData);
+ byte[] prvBytes = b64Decoder.decode(prvData);
+ String password = null;
+ if ((GenericUtils.length(prvEncryption) > 0)
+ && (!NO_PRIVATE_KEY_ENCRYPTION_VALUE.equalsIgnoreCase(prvEncryption))) {
+ password = passwordProvider.getPassword(resourceKey);
+ }
+
+ if (GenericUtils.isEmpty(prvEncryption)
+ || NO_PRIVATE_KEY_ENCRYPTION_VALUE.equalsIgnoreCase(prvEncryption)
+ || GenericUtils.isEmpty(password)) {
+ return loadKeyPairs(resourceKey, pubBytes, prvBytes);
+ }
+
+ // format is "<cipher><bits>-<mode>" - e.g., "aes256-cbc"
+ int pos = prvEncryption.indexOf('-');
+ if (pos <= 0) {
+ throw new StreamCorruptedException("Missing private key encryption mode in " + prvEncryption);
+ }
+
+ String mode = prvEncryption.substring(pos + 1).toUpperCase();
+ String algName = null;
+ int numBits = 0;
+ for (int index = 0; index < pos; index++) {
+ char ch = prvEncryption.charAt(index);
+ if ((ch >= '0') && (ch <= '9')) {
+ algName = prvEncryption.substring(0, index).toUpperCase();
+ numBits = Integer.parseInt(prvEncryption.substring(index, pos));
+ break;
+ }
+ }
+
+ if (GenericUtils.isEmpty(algName) || (numBits <= 0)) {
+ throw new StreamCorruptedException("Missing private key encryption algorithm details in " + prvEncryption);
+ }
+
+ prvBytes = PuttyKeyPairResourceParser.decodePrivateKeyBytes(prvBytes, algName, numBits, mode, password);
+ return loadKeyPairs(resourceKey, pubBytes, prvBytes);
+ }
+
+ public Collection<KeyPair> loadKeyPairs(String resourceKey, byte[] pubData, byte[] prvData)
+ throws IOException, GeneralSecurityException {
+ ValidateUtils.checkNotNullAndNotEmpty(pubData, "No public key data in %s", resourceKey);
+ ValidateUtils.checkNotNullAndNotEmpty(prvData, "No private key data in %s", resourceKey);
+ try (InputStream pubStream = new ByteArrayInputStream(pubData);
+ InputStream prvStream = new ByteArrayInputStream(prvData)) {
+ return loadKeyPairs(resourceKey, pubStream, prvStream);
+ }
+ }
+
+ public Collection<KeyPair> loadKeyPairs(String resourceKey, InputStream pubData, InputStream prvData)
+ throws IOException, GeneralSecurityException {
+ try (PuttyKeyReader pubReader =
+ new PuttyKeyReader(ValidateUtils.checkNotNull(pubData, "No public key data in %s", resourceKey));
+ PuttyKeyReader prvReader =
+ new PuttyKeyReader(ValidateUtils.checkNotNull(prvData, "No private key data in %s", resourceKey))) {
+ return loadKeyPairs(resourceKey, pubReader, prvReader);
+ }
+ }
+
+ public abstract Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader)
+ throws IOException, GeneralSecurityException;
+}
[26/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java
deleted file mode 100644
index 28a3d1f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java
+++ /dev/null
@@ -1,170 +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.client.config.hosts;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Base64;
-import java.util.Objects;
-
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.RuntimeSshException;
-import org.apache.sshd.common.mac.Mac;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class KnownHostHashValue {
- /**
- * Character used to indicate a hashed host pattern
- */
- public static final char HASHED_HOST_DELIMITER = '|';
-
- public static final NamedFactory<Mac> DEFAULT_DIGEST = KnownHostDigest.SHA1;
-
- private NamedFactory<Mac> digester = DEFAULT_DIGEST;
- private byte[] saltValue;
- private byte[] digestValue;
-
- public KnownHostHashValue() {
- super();
- }
-
- public NamedFactory<Mac> getDigester() {
- return digester;
- }
-
- public void setDigester(NamedFactory<Mac> digester) {
- this.digester = digester;
- }
-
- public byte[] getSaltValue() {
- return saltValue;
- }
-
- public void setSaltValue(byte[] saltValue) {
- this.saltValue = saltValue;
- }
-
- public byte[] getDigestValue() {
- return digestValue;
- }
-
- public void setDigestValue(byte[] digestValue) {
- this.digestValue = digestValue;
- }
-
- /**
- * Checks if the host matches the hash
- *
- * @param host The host name/address - ignored if {@code null}/empty
- * @return {@code true} if host matches the hash
- * @throws RuntimeException If entry not properly initialized
- */
- public boolean isHostMatch(String host) {
- if (GenericUtils.isEmpty(host)) {
- return false;
- }
-
- try {
- byte[] expected = getDigestValue();
- byte[] actual = calculateHashValue(host, getDigester(), getSaltValue());
- return Arrays.equals(expected, actual);
- } catch (Throwable t) {
- if (t instanceof RuntimeException) {
- throw (RuntimeException) t;
- }
- throw new RuntimeSshException("Failed (" + t.getClass().getSimpleName() + ")"
- + " to calculate hash value: " + t.getMessage(), t);
- }
- }
-
- @Override
- public String toString() {
- if ((getDigester() == null) || NumberUtils.isEmpty(getSaltValue()) || NumberUtils.isEmpty(getDigestValue())) {
- return Objects.toString(getDigester(), null)
- + "-" + BufferUtils.toHex(':', getSaltValue())
- + "-" + BufferUtils.toHex(':', getDigestValue());
- }
-
- try {
- return append(new StringBuilder(Byte.MAX_VALUE), this).toString();
- } catch (IOException | RuntimeException e) { // unexpected
- return e.getClass().getSimpleName() + ": " + e.getMessage();
- }
- }
-
- // see http://nms.lcs.mit.edu/projects/ssh/README.hashed-hosts
- public static byte[] calculateHashValue(String host, Factory<? extends Mac> factory, byte[] salt) throws Exception {
- return calculateHashValue(host, factory.create(), salt);
- }
-
- public static byte[] calculateHashValue(String host, Mac mac, byte[] salt) throws Exception {
- mac.init(salt);
-
- byte[] hostBytes = host.getBytes(StandardCharsets.UTF_8);
- mac.update(hostBytes);
- return mac.doFinal();
- }
-
- public static <A extends Appendable> A append(A sb, KnownHostHashValue hashValue) throws IOException {
- return (hashValue == null) ? sb : append(sb, hashValue.getDigester(), hashValue.getSaltValue(), hashValue.getDigestValue());
- }
-
- public static <A extends Appendable> A append(A sb, NamedResource factory, byte[] salt, byte[] digest) throws IOException {
- Base64.Encoder encoder = Base64.getEncoder();
- sb.append(HASHED_HOST_DELIMITER).append(factory.getName());
- sb.append(HASHED_HOST_DELIMITER).append(encoder.encodeToString(salt));
- sb.append(HASHED_HOST_DELIMITER).append(encoder.encodeToString(digest));
- return sb;
- }
-
- public static KnownHostHashValue parse(String patternString) {
- String pattern = GenericUtils.replaceWhitespaceAndTrim(patternString);
- return parse(pattern, GenericUtils.isEmpty(pattern) ? null : new KnownHostHashValue());
- }
-
- public static <V extends KnownHostHashValue> V parse(String patternString, V value) {
- String pattern = GenericUtils.replaceWhitespaceAndTrim(patternString);
- if (GenericUtils.isEmpty(pattern)) {
- return value;
- }
-
- String[] components = GenericUtils.split(pattern, HASHED_HOST_DELIMITER);
- ValidateUtils.checkTrue(components.length == 4 /* 1st one is empty */, "Invalid hash pattern (insufficient data): %s", pattern);
- ValidateUtils.checkTrue(GenericUtils.isEmpty(components[0]), "Invalid hash pattern (unexpected extra data): %s", pattern);
-
- NamedFactory<Mac> factory =
- ValidateUtils.checkNotNull(KnownHostDigest.fromName(components[1]),
- "Invalid hash pattern (unknown digest): %s", pattern);
- Base64.Decoder decoder = Base64.getDecoder();
- value.setDigester(factory);
- value.setSaltValue(decoder.decode(components[2]));
- value.setDigestValue(decoder.decode(components[3]));
- return value;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
deleted file mode 100644
index ab9929b..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
+++ /dev/null
@@ -1,106 +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.client.config.keys;
-
-import java.nio.file.Path;
-import java.security.KeyPair;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.function.Supplier;
-
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.config.keys.BuiltinIdentities;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BuiltinClientIdentitiesWatcher extends ClientIdentitiesWatcher {
- private final boolean supportedOnly;
-
- public BuiltinClientIdentitiesWatcher(Path keysFolder, boolean supportedOnly,
- ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
- this(keysFolder, NamedResource.getNameList(BuiltinIdentities.VALUES), supportedOnly, loader, provider, strict);
- }
-
- public BuiltinClientIdentitiesWatcher(Path keysFolder, Collection<String> ids, boolean supportedOnly,
- ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
- this(keysFolder, ids, supportedOnly,
- GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
- GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
- strict);
- }
-
- public BuiltinClientIdentitiesWatcher(Path keysFolder, boolean supportedOnly,
- Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
- this(keysFolder, NamedResource.getNameList(BuiltinIdentities.VALUES), supportedOnly, loader, provider, strict);
- }
-
- public BuiltinClientIdentitiesWatcher(Path keysFolder, Collection<String> ids, boolean supportedOnly,
- Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
- super(getBuiltinIdentitiesPaths(keysFolder, ids), loader, provider, strict);
- this.supportedOnly = supportedOnly;
- }
-
- public final boolean isSupportedOnly() {
- return supportedOnly;
- }
-
- @Override
- public Iterable<KeyPair> loadKeys() {
- return isSupportedOnly() ? loadKeys(this::isSupported) : super.loadKeys();
- }
-
- private boolean isSupported(KeyPair kp) {
- BuiltinIdentities id = BuiltinIdentities.fromKeyPair(kp);
- if ((id != null) && id.isSupported()) {
- return true;
- }
- if (log.isDebugEnabled()) {
- log.debug("loadKeys - remove unsupported identity={}, key-type={}, key={}",
- id, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
- }
- return false;
- }
-
- public static List<Path> getDefaultBuiltinIdentitiesPaths(Path keysFolder) {
- return getBuiltinIdentitiesPaths(keysFolder, NamedResource.getNameList(BuiltinIdentities.VALUES));
- }
-
- public static List<Path> getBuiltinIdentitiesPaths(Path keysFolder, Collection<String> ids) {
- Objects.requireNonNull(keysFolder, "No keys folder");
- if (GenericUtils.isEmpty(ids)) {
- return Collections.emptyList();
- }
-
- List<Path> paths = new ArrayList<>(ids.size());
- for (String id : ids) {
- String fileName = ClientIdentity.getIdentityFileName(id);
- paths.add(keysFolder.resolve(fileName));
- }
-
- return paths;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
deleted file mode 100644
index 094766f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
+++ /dev/null
@@ -1,139 +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.client.config.keys;
-
-import java.nio.file.Path;
-import java.security.KeyPair;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-import java.util.stream.Stream;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * Watches over a group of files that contains client identities
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ClientIdentitiesWatcher extends AbstractKeyPairProvider implements KeyPairProvider {
- private final Collection<ClientIdentityProvider> providers;
-
- public ClientIdentitiesWatcher(Collection<? extends Path> paths,
- ClientIdentityLoader loader, FilePasswordProvider provider) {
- this(paths, loader, provider, true);
- }
-
- public ClientIdentitiesWatcher(Collection<? extends Path> paths,
- ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
- this(paths,
- GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
- GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
- strict);
- }
-
- public ClientIdentitiesWatcher(Collection<? extends Path> paths,
- Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
- this(paths, loader, provider, true);
- }
-
- public ClientIdentitiesWatcher(Collection<? extends Path> paths,
- Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
- this(buildProviders(paths, loader, provider, strict));
- }
-
- public ClientIdentitiesWatcher(Collection<ClientIdentityProvider> providers) {
- this.providers = providers;
- }
-
- @Override
- public Iterable<KeyPair> loadKeys() {
- return loadKeys(null);
- }
-
- protected Iterable<KeyPair> loadKeys(Predicate<? super KeyPair> filter) {
- return () -> {
- Stream<KeyPair> stream = safeMap(GenericUtils.stream(providers), this::doGetKeyPair);
- if (filter != null) {
- stream = stream.filter(filter);
- }
- return stream.iterator();
- };
- }
-
- /**
- * Performs a mapping operation on the stream, discarding any null values
- * returned by the mapper.
- *
- * @param <U> Original type
- * @param <V> Mapped type
- * @param stream Original values stream
- * @param mapper Mapper to target type
- * @return Mapped stream
- */
- protected <U, V> Stream<V> safeMap(Stream<U> stream, Function<? super U, ? extends V> mapper) {
- return stream.map(u -> Optional.ofNullable(mapper.apply(u)))
- .filter(Optional::isPresent)
- .map(Optional::get);
- }
-
- protected KeyPair doGetKeyPair(ClientIdentityProvider p) {
- try {
- KeyPair kp = p.getClientIdentity();
- if (kp == null) {
- if (log.isDebugEnabled()) {
- log.debug("loadKeys({}) no key loaded", p);
- }
- }
- return kp;
- } catch (Throwable e) {
- log.warn("loadKeys({}) failed ({}) to load key: {}", p, e.getClass().getSimpleName(), e.getMessage());
- if (log.isDebugEnabled()) {
- log.debug("loadKeys(" + p + ") key load failure details", e);
- }
- return null;
- }
- }
-
- public static List<ClientIdentityProvider> buildProviders(
- Collection<? extends Path> paths, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
- return buildProviders(paths,
- GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
- GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
- strict);
- }
-
- public static List<ClientIdentityProvider> buildProviders(
- Collection<? extends Path> paths, Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
- if (GenericUtils.isEmpty(paths)) {
- return Collections.emptyList();
- }
-
- return GenericUtils.map(paths, p -> new ClientIdentityFileWatcher(p, loader, provider, strict));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
deleted file mode 100644
index 9060fec..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
+++ /dev/null
@@ -1,325 +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.client.config.keys;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.function.Function;
-
-import org.apache.sshd.client.SshClient;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.config.keys.BuiltinIdentities;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.IdentityUtils;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.config.keys.PublicKeyEntry;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.FileInfoExtractor;
-import org.apache.sshd.common.util.io.IoUtils;
-
-/**
- * Provides keys loading capability from the user's keys folder - e.g., {@code id_rsa}
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see org.apache.sshd.common.util.security.SecurityUtils#getKeyPairResourceParser()
- */
-public final class ClientIdentity {
-
- public static final String ID_FILE_PREFIX = "id_";
-
- public static final String ID_FILE_SUFFIX = "";
-
- public static final Function<String, String> ID_GENERATOR =
- ClientIdentity::getIdentityFileName;
-
- private ClientIdentity() {
- throw new UnsupportedOperationException("No instance");
- }
-
- /**
- * @param name The file name - ignored if {@code null}/empty
- * @return The identity type - {@code null} if cannot determine it - e.g.,
- * does not start with the {@link #ID_FILE_PREFIX}
- */
- public static String getIdentityType(String name) {
- if (GenericUtils.isEmpty(name)
- || (name.length() <= ID_FILE_PREFIX.length())
- || (!name.startsWith(ID_FILE_PREFIX))) {
- return null;
- } else {
- return name.substring(ID_FILE_PREFIX.length());
- }
- }
-
- public static String getIdentityFileName(NamedResource r) {
- return getIdentityFileName((r == null) ? null : r.getName());
- }
-
- /**
- * @param type The identity type - e.g., {@code rsa} - ignored
- * if {@code null}/empty
- * @return The matching file name for the identity - {@code null}
- * if no name
- * @see #ID_FILE_PREFIX
- * @see #ID_FILE_SUFFIX
- * @see IdentityUtils#getIdentityFileName(String, String, String)
- */
- public static String getIdentityFileName(String type) {
- return IdentityUtils.getIdentityFileName(ID_FILE_PREFIX, type, ID_FILE_SUFFIX);
- }
-
- /**
- * @param <C> The generic client class
- * @param client The {@link SshClient} to updated
- * @param strict If {@code true} then files that do not have the required
- * access rights are excluded from consideration
- * @param supportedOnly If {@code true} then ignore identities that are not
- * supported internally
- * @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 LinkOption}s to apply when checking
- * for existence
- * @return The updated <tt>client</tt> instance - provided a non-{@code null}
- * {@link KeyPairProvider} was generated
- * @throws IOException If failed to access the file system
- * @throws GeneralSecurityException If failed to load the keys
- * @see #setKeyPairProvider(SshClient, Path, boolean, boolean, FilePasswordProvider, LinkOption...)
- */
- public static <C extends SshClient> C setKeyPairProvider(
- C client, boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
- throws IOException, GeneralSecurityException {
- return setKeyPairProvider(client, PublicKeyEntry.getDefaultKeysFolderPath(), strict, supportedOnly, provider, options);
- }
-
- /**
- * @param <C> The generic client class
- * @param client The {@link SshClient} to updated
- * @param dir The folder to scan for the built-in identities
- * @param strict If {@code true} then files that do not have the required
- * access rights are excluded from consideration
- * @param supportedOnly If {@code true} then ignore identities that are not
- * supported internally
- * @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 LinkOption}s to apply when checking
- * for existence
- * @return The updated <tt>client</tt> instance - provided a non-{@code null}
- * {@link KeyPairProvider} was generated
- * @throws IOException If failed to access the file system
- * @throws GeneralSecurityException If failed to load the keys
- * @see #loadDefaultKeyPairProvider(Path, boolean, boolean, FilePasswordProvider, LinkOption...)
- */
- public static <C extends SshClient> C setKeyPairProvider(
- C client, Path dir, boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
- throws IOException, GeneralSecurityException {
- KeyPairProvider kpp = loadDefaultKeyPairProvider(dir, strict, supportedOnly, provider, options);
- if (kpp != null) {
- client.setKeyPairProvider(kpp);
- }
-
- return client;
- }
-
- /**
- * @param strict If {@code true} then files that do not have the required
- * access rights are excluded from consideration
- * @param supportedOnly If {@code true} then ignore identities that are not
- * supported internally
- * @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 LinkOption}s to apply when checking
- * for existence
- * @return A {@link KeyPair} for the identities - {@code null} if no identities
- * available (e.g., after filtering unsupported ones or strict permissions)
- * @throws IOException If failed to access the file system
- * @throws GeneralSecurityException If failed to load the keys
- * @see PublicKeyEntry#getDefaultKeysFolderPath()
- * @see #loadDefaultIdentities(Path, boolean, FilePasswordProvider, LinkOption...)
- */
- public static KeyPairProvider loadDefaultKeyPairProvider(
- boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
- throws IOException, GeneralSecurityException {
- return loadDefaultKeyPairProvider(PublicKeyEntry.getDefaultKeysFolderPath(), strict, supportedOnly, provider, options);
- }
-
- /**
- * @param dir The folder to scan for the built-in identities
- * @param strict If {@code true} then files that do not have the required
- * access rights are excluded from consideration
- * @param supportedOnly If {@code true} then ignore identities that are not
- * supported internally
- * @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 LinkOption}s to apply when checking
- * for existence
- * @return A {@link KeyPair} for the identities - {@code null} if no identities
- * available (e.g., after filtering unsupported ones or strict permissions)
- * @throws IOException If failed to access the file system
- * @throws GeneralSecurityException If failed to load the keys
- * @see #loadDefaultIdentities(Path, boolean, FilePasswordProvider, LinkOption...)
- * @see IdentityUtils#createKeyPairProvider(Map, boolean)
- */
- public static KeyPairProvider loadDefaultKeyPairProvider(
- Path dir, boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
- throws IOException, GeneralSecurityException {
- Map<String, KeyPair> ids = loadDefaultIdentities(dir, strict, provider, options);
- return IdentityUtils.createKeyPairProvider(ids, supportedOnly);
- }
-
- /**
- * @param strict If {@code true} then files that do not have the required
- * access rights are excluded from consideration
- * @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 LinkOption}s to apply when checking
- * for existence
- * @return A {@link Map} of the found files 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 PublicKeyEntry#getDefaultKeysFolderPath()
- * @see #loadDefaultIdentities(Path, boolean, FilePasswordProvider, LinkOption...)
- */
- public static Map<String, KeyPair> loadDefaultIdentities(boolean strict, FilePasswordProvider provider, LinkOption... options)
- throws IOException, GeneralSecurityException {
- return loadDefaultIdentities(PublicKeyEntry.getDefaultKeysFolderPath(), strict, provider, options);
- }
-
- /**
- * @param dir The folder to scan for the built-in identities
- * @param strict If {@code true} then files that do not have the required
- * access rights are excluded from consideration
- * @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 LinkOption}s to apply when checking
- * for existence
- * @return A {@link Map} of the found files 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 #loadIdentities(Path, boolean, Collection, Function, FilePasswordProvider, LinkOption...)
- * @see BuiltinIdentities
- */
- public static Map<String, KeyPair> loadDefaultIdentities(Path dir, boolean strict, FilePasswordProvider provider, LinkOption... options)
- throws IOException, GeneralSecurityException {
- return loadIdentities(dir, strict, BuiltinIdentities.NAMES, ID_GENERATOR, provider, options);
- }
-
- /**
- * Scans a folder and loads all available identity files
- *
- * @param dir The {@link Path} of the folder to scan - ignored if not exists
- * @param strict If {@code true} then files that do not have the required
- * access rights are excluded from consideration
- * @param types The identity types - ignored if {@code null}/empty
- * @param idGenerator A {@link Function} to derive the file name
- * holding the specified type
- * @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 LinkOption}s to apply when checking
- * for existence
- * @return A {@link Map} of the found files 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 #scanIdentitiesFolder(Path, boolean, Collection, Function, LinkOption...)
- * @see IdentityUtils#loadIdentities(Map, FilePasswordProvider, java.nio.file.OpenOption...)
- */
- public static Map<String, KeyPair> loadIdentities(
- Path dir, boolean strict, Collection<String> types, Function<String, String> idGenerator, FilePasswordProvider provider, LinkOption... options)
- throws IOException, GeneralSecurityException {
- Map<String, Path> paths = scanIdentitiesFolder(dir, strict, types, idGenerator, options);
- return IdentityUtils.loadIdentities(paths, provider, IoUtils.EMPTY_OPEN_OPTIONS);
- }
-
- /**
- * Scans a folder for possible identity files
- *
- * @param dir The {@link Path} of the folder to scan - ignored if not exists
- * @param strict If {@code true} then files that do not have the required
- * access rights are excluded from consideration
- * @param types The identity types - ignored if {@code null}/empty
- * @param idGenerator A {@link Function} to derive the file name
- * holding the specified type
- * @param options The {@link LinkOption}s to apply when checking
- * for existence
- * @return A {@link Map} of the found files where key=identity type (case
- * <U>insensitive</U>), value=the {@link Path} of the file holding the key
- * @throws IOException If failed to access the file system
- * @see KeyUtils#validateStrictKeyFilePermissions(Path, LinkOption...)
- */
- public static Map<String, Path> scanIdentitiesFolder(
- Path dir, boolean strict, Collection<String> types, Function<String, String> idGenerator, LinkOption... options)
- throws IOException {
- if (GenericUtils.isEmpty(types)) {
- return Collections.emptyMap();
- }
-
- if (!Files.exists(dir, options)) {
- return Collections.emptyMap();
- }
-
- ValidateUtils.checkTrue(FileInfoExtractor.ISDIR.infoOf(dir, options), "Not a directory: %s", dir);
-
- Map<String, Path> paths = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- for (String t : types) {
- String fileName = idGenerator.apply(t);
- Path p = dir.resolve(fileName);
- if (!Files.exists(p, options)) {
- continue;
- }
-
- if (strict) {
- if (KeyUtils.validateStrictKeyFilePermissions(p, options) != null) {
- continue;
- }
- }
-
- Path prev = paths.put(t, p);
- ValidateUtils.checkTrue(prev == null, "Multiple mappings for type=%s", t);
- }
-
- return paths;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
deleted file mode 100644
index a00cb24..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
+++ /dev/null
@@ -1,141 +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.client.config.keys;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Supplier;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.ModifiableFileWatcher;
-
-/**
- * A {@link ClientIdentityProvider} that watches a given key file re-loading
- * its contents if it is ever modified, deleted or (re-)created
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ClientIdentityFileWatcher extends ModifiableFileWatcher implements ClientIdentityProvider {
- private final AtomicReference<KeyPair> identityHolder = new AtomicReference<>(null);
- private final Supplier<ClientIdentityLoader> loaderHolder;
- private final Supplier<FilePasswordProvider> providerHolder;
- private final boolean strict;
-
- public ClientIdentityFileWatcher(Path path, ClientIdentityLoader loader, FilePasswordProvider provider) {
- this(path, loader, provider, true);
- }
-
- public ClientIdentityFileWatcher(Path path, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
- this(path,
- GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
- GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
- strict);
- }
-
- public ClientIdentityFileWatcher(Path path, Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
- this(path, loader, provider, true);
- }
-
- public ClientIdentityFileWatcher(Path path, Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
- super(path);
- this.loaderHolder = Objects.requireNonNull(loader, "No client identity loader");
- this.providerHolder = Objects.requireNonNull(provider, "No password provider");
- this.strict = strict;
- }
-
- public final boolean isStrict() {
- return strict;
- }
-
- public final ClientIdentityLoader getClientIdentityLoader() {
- return loaderHolder.get();
- }
-
- public final FilePasswordProvider getFilePasswordProvider() {
- return providerHolder.get();
- }
-
- @Override
- public KeyPair getClientIdentity() throws IOException, GeneralSecurityException {
- if (checkReloadRequired()) {
- KeyPair kp = identityHolder.getAndSet(null); // start fresh
- Path path = getPath();
-
- if (exists()) {
- KeyPair id = reloadClientIdentity(path);
- if (!KeyUtils.compareKeyPairs(kp, id)) {
- if (log.isDebugEnabled()) {
- log.debug("getClientIdentity({}) identity {}", path, (kp == null) ? "loaded" : "re-loaded");
- }
- }
-
- updateReloadAttributes();
- identityHolder.set(id);
- }
- }
-
- return identityHolder.get();
- }
-
- protected KeyPair reloadClientIdentity(Path path) throws IOException, GeneralSecurityException {
- if (isStrict()) {
- Map.Entry<String, Object> violation = KeyUtils.validateStrictKeyFilePermissions(path, IoUtils.EMPTY_LINK_OPTIONS);
- if (violation != null) {
- if (log.isDebugEnabled()) {
- log.debug("reloadClientIdentity({}) ignore due to {}", path, violation.getKey());
- }
- return null;
- }
- }
-
- String location = path.toString();
- ClientIdentityLoader idLoader = Objects.requireNonNull(getClientIdentityLoader(), "No client identity loader");
- if (idLoader.isValidLocation(location)) {
- KeyPair kp = idLoader.loadClientIdentity(location, Objects.requireNonNull(getFilePasswordProvider(), "No file password provider"));
- if (log.isTraceEnabled()) {
- PublicKey key = (kp == null) ? null : kp.getPublic();
- if (key != null) {
- log.trace("reloadClientIdentity({}) loaded {}-{}",
- location, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
-
- } else {
- log.trace("reloadClientIdentity({}) no key loaded", location);
- }
- }
-
- return kp;
- }
-
- if (log.isDebugEnabled()) {
- log.debug("reloadClientIdentity({}) invalid location", location);
- }
-
- return null;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java
deleted file mode 100644
index 8b3a295..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java
+++ /dev/null
@@ -1,95 +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.client.config.keys;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface ClientIdentityLoader {
- /**
- * <P>A default implementation that assumes a file location that <U>must</U> exist.</P>
- *
- * <P>
- * <B>Note:</B> It calls {@link SecurityUtils#loadKeyPairIdentity(String, InputStream, FilePasswordProvider)}
- * </P>
- */
- ClientIdentityLoader DEFAULT = new ClientIdentityLoader() {
- @Override
- public boolean isValidLocation(String location) throws IOException {
- Path path = toPath(location);
- return Files.exists(path, IoUtils.EMPTY_LINK_OPTIONS);
- }
-
- @Override
- public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
- Path path = toPath(location);
- try (InputStream inputStream = Files.newInputStream(path, IoUtils.EMPTY_OPEN_OPTIONS)) {
- return SecurityUtils.loadKeyPairIdentity(path.toString(), inputStream, provider);
- }
- }
-
- @Override
- public String toString() {
- return "DEFAULT";
- }
-
- private Path toPath(String location) {
- Path path = Paths.get(ValidateUtils.checkNotNullAndNotEmpty(location, "No location"));
- path = path.toAbsolutePath();
- path = path.normalize();
- return path;
- }
- };
-
- /**
- * @param location The identity key-pair location - the actual meaning (file, URL, etc.)
- * depends on the implementation.
- * @return {@code true} if it represents a valid location - the actual meaning of
- * the validity depends on the implementation
- * @throws IOException If failed to validate the location
- */
- boolean isValidLocation(String location) throws IOException;
-
- /**
- * @param location The identity key-pair location - the actual meaning (file, URL, etc.)
- * depends on the implementation.
- * @param provider The {@link FilePasswordProvider} to consult if the location contains
- * an encrypted identity
- * @return The loaded {@link KeyPair} - {@code null} if location is empty
- * and it is OK that it does not exist
- * @throws IOException If failed to access / process the remote location
- * @throws GeneralSecurityException If failed to convert the contents into
- * a valid identity
- */
- KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
deleted file mode 100644
index bda4700..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.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.client.config.keys;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface ClientIdentityProvider {
- /**
- * Provides a {@link KeyPair} representing the client identity
- *
- * @return The client identity - may be {@code null} if no currently
- * available identity from this provider. <B>Note:</B> the provider
- * may return a <U>different</U> value every time this method is called
- * - e.g., if it is (re-)loading contents from a file.
- * @throws IOException If failed to load the identity
- * @throws GeneralSecurityException If failed to parse the identity
- */
- KeyPair getClientIdentity() throws IOException, GeneralSecurityException;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java b/sshd-core/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
deleted file mode 100644
index 3afa129..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
+++ /dev/null
@@ -1,66 +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.client.config.keys;
-
-import java.nio.file.Path;
-import java.util.List;
-import java.util.Objects;
-import java.util.function.Supplier;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.PublicKeyEntry;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DefaultClientIdentitiesWatcher extends BuiltinClientIdentitiesWatcher {
- public DefaultClientIdentitiesWatcher(ClientIdentityLoader loader, FilePasswordProvider provider) {
- this(loader, provider, true);
- }
-
- public DefaultClientIdentitiesWatcher(ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
- this(true, loader, provider, strict);
- }
-
- public DefaultClientIdentitiesWatcher(boolean supportedOnly, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
- this(supportedOnly,
- GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
- GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
- strict);
- }
-
- public DefaultClientIdentitiesWatcher(Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
- this(loader, provider, true);
- }
-
- public DefaultClientIdentitiesWatcher(Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
- this(true, loader, provider, strict);
- }
-
- public DefaultClientIdentitiesWatcher(boolean supportedOnly,
- Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
- super(PublicKeyEntry.getDefaultKeysFolderPath(), supportedOnly, loader, provider, strict);
- }
-
- public static List<Path> getDefaultBuiltinIdentitiesPaths() {
- return getDefaultBuiltinIdentitiesPaths(PublicKeyEntry.getDefaultKeysFolderPath());
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java b/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java
index 7a5052d..c4cb849 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java
@@ -46,7 +46,7 @@ import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.Factory;
import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.config.keys.PublicKeyEntry;
@@ -350,7 +350,7 @@ public class KnownHostsServerKeyVerifier
continue;
}
- int pos = line.indexOf(SshConfigFileReader.COMMENT_CHAR);
+ int pos = line.indexOf(ConfigFileReaderSupport.COMMENT_CHAR);
if (pos == 0) {
lines.add(line);
continue;
@@ -703,7 +703,7 @@ public class KnownHostsServerKeyVerifier
KnownHostHashValue.append(sb, digester, salt, digestValue);
} else {
int portValue = hostIdentity.getPort();
- boolean nonDefaultPort = (portValue > 0) && (portValue != SshConfigFileReader.DEFAULT_PORT);
+ boolean nonDefaultPort = (portValue > 0) && (portValue != ConfigFileReaderSupport.DEFAULT_PORT);
if (nonDefaultPort) {
sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
index 1d68a20..31202bb 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
@@ -27,14 +27,17 @@ import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.rmi.RemoteException;
import java.rmi.ServerException;
+import java.security.KeyPair;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
+import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.sshd.client.ClientAuthenticationManager;
import org.apache.sshd.client.ClientFactoryManager;
+import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
import org.apache.sshd.client.channel.ChannelDirectTcpip;
import org.apache.sshd.client.channel.ChannelExec;
import org.apache.sshd.client.channel.ChannelShell;
@@ -46,6 +49,7 @@ import org.apache.sshd.client.session.forward.DynamicPortForwardingTracker;
import org.apache.sshd.client.session.forward.ExplicitPortForwardingTracker;
import org.apache.sshd.common.forward.PortForwardingManager;
import org.apache.sshd.common.future.KeyExchangeFuture;
+import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
import org.apache.sshd.common.session.Session;
import org.apache.sshd.common.util.io.NoCloseOutputStream;
import org.apache.sshd.common.util.io.NullOutputStream;
@@ -353,4 +357,54 @@ public interface ClientSession
* @throws IOException if a key exchange is already running
*/
KeyExchangeFuture switchToNoneCipher() throws IOException;
+
+ /**
+ * Creates a "unified" {@link KeyIdentityProvider} of key pairs out of the registered
+ * {@link KeyPair} identities and the extra available ones as a single iterator
+ * of key pairs
+ *
+ *
+ * @param session The {@link ClientSession} - ignored if {@code null} (i.e., empty
+ * iterator returned)
+ * @return The wrapping KeyIdentityProvider
+ * @see ClientSession#getRegisteredIdentities()
+ * @see ClientSession#getKeyPairProvider()
+ */
+ static KeyIdentityProvider providerOf(ClientSession session) {
+ return session == null
+ ? KeyIdentityProvider.EMPTY_KEYS_PROVIDER
+ : KeyIdentityProvider.resolveKeyIdentityProvider(
+ session.getRegisteredIdentities(), session.getKeyPairProvider());
+ }
+
+ /**
+ * Creates a "unified" {@link Iterator} of key pairs out of the registered
+ * {@link KeyPair} identities and the extra available ones as a single iterator
+ * of key pairs
+ *
+ * @param session The {@link ClientSession} - ignored if {@code null} (i.e., empty
+ * iterator returned)
+ * @return The wrapping iterator
+ * @see ClientSession#getRegisteredIdentities()
+ * @see ClientSession#getKeyPairProvider()
+ */
+ static Iterator<KeyPair> keyPairIteratorOf(ClientSession session) {
+ return KeyIdentityProvider.iteratorOf(providerOf(session));
+ }
+
+ /**
+ * Creates a "unified" {@link Iterator} of passwords out of the registered
+ * passwords and the extra available ones as a single iterator of passwords
+ *
+ * @param session The {@link ClientSession} - ignored if {@code null} (i.e., empty
+ * iterator returned)
+ * @return The wrapping iterator
+ * @see ClientSession#getRegisteredIdentities()
+ * @see ClientSession#getPasswordIdentityProvider()
+ */
+ static Iterator<String> passwordIteratorOf(ClientSession session) {
+ return (session == null)
+ ? Collections.<String>emptyIterator()
+ : PasswordIdentityProvider.iteratorOf(session.getRegisteredIdentities(), session.getPasswordIdentityProvider());
+ }
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/simple/SimpleClientConfigurator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/simple/SimpleClientConfigurator.java b/sshd-core/src/main/java/org/apache/sshd/client/simple/SimpleClientConfigurator.java
index 58913b4..31a994b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/simple/SimpleClientConfigurator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/simple/SimpleClientConfigurator.java
@@ -19,7 +19,7 @@
package org.apache.sshd.client.simple;
-import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.ConfigFileReaderSupport;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
@@ -35,7 +35,7 @@ public interface SimpleClientConfigurator {
*/
long DEFAULT_AUTHENTICATION_TIMEOUT = Long.MAX_VALUE; // virtually infinite
- int DEFAULT_PORT = SshConfigFileReader.DEFAULT_PORT;
+ int DEFAULT_PORT = ConfigFileReaderSupport.DEFAULT_PORT;
/**
* @return Current connect timeout (msec.) - always positive
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/AttributeStore.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/AttributeStore.java b/sshd-core/src/main/java/org/apache/sshd/common/AttributeStore.java
deleted file mode 100644
index 2b58d94..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/AttributeStore.java
+++ /dev/null
@@ -1,152 +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;
-
-import java.util.Objects;
-
-import org.apache.sshd.common.channel.Channel;
-import org.apache.sshd.common.session.Session;
-
-/**
- * Provides the capability to attach in-memory attributes to the entity
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface AttributeStore {
- /**
- * <P>
- * Type safe key for storage of user attributes. Typically it is used as a static
- * variable that is shared between the producer and the consumer. To further
- * restrict access the setting or getting it from the store one can add static
- * {@code get/set methods} e.g:
- * </P>
- *
- * <pre>
- * public static final AttributeKey<MyValue> MY_KEY = new AttributeKey<MyValue>();
- *
- * public static MyValue getMyValue(Session s) {
- * return s.getAttribute(MY_KEY);
- * }
- *
- * public static void setMyValue(Session s, MyValue value) {
- * s.setAttribute(MY_KEY, value);
- * }
- * </pre>
- *
- * @param <T> type of value stored in the attribute.
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
- // CHECKSTYLE:OFF
- class AttributeKey<T> {
- public AttributeKey() {
- super();
- }
- }
- // CHECKSTYLE:ON
-
- /**
- * Returns the value of the user-defined attribute.
- *
- * @param <T> The generic attribute type
- * @param key The key of the attribute; must not be {@code null}.
- * @return {@code null} if there is no value associated with the specified key
- */
- <T> T getAttribute(AttributeKey<T> key);
-
- /**
- * Sets a user-defined attribute.
- *
- * @param <T> The generic attribute type
- * @param key The key of the attribute; must not be {@code null}.
- * @param value The value of the attribute; must not be {@code null}.
- * @return The old value of the attribute; {@code null} if it is new.
- */
- <T> T setAttribute(AttributeKey<T> key, T value);
-
- /**
- * Removes the user-defined attribute
- *
- * @param <T> The generic attribute type
- * @param key The key of the attribute; must not be {@code null}.
- * @return The removed value; {@code null} if no previous value
- */
- <T> T removeAttribute(AttributeKey<T> key);
-
- /**
- * Attempts to resolve the associated value by going up the store's
- * hierarchy (if any)
- *
- * @param <T> The generic attribute type
- * @param key The key of the attribute; must not be {@code null}.
- * @return {@code null} if there is no value associated with the specified key
- */
- <T> T resolveAttribute(AttributeKey<T> key);
-
- /**
- * @param <T> The generic attribute type
- * @param manager The {@link FactoryManager} - ignored if {@code null}
- * @param key The attribute key - never {@code null}
- * @return Associated value - {@code null} if not found
- */
- static <T> T resolveAttribute(FactoryManager manager, AttributeKey<T> key) {
- Objects.requireNonNull(key, "No key");
- return (manager == null) ? null : manager.getAttribute(key);
- }
-
- /**
- * Attempts to use the session's attribute, if not found then tries the factory manager
- *
- * @param <T> The generic attribute type
- * @param session The {@link Session} - ignored if {@code null}
- * @param key The attribute key - never {@code null}
- * @return Associated value - {@code null} if not found
- * @see Session#getFactoryManager()
- * @see #resolveAttribute(FactoryManager, AttributeKey)
- */
- static <T> T resolveAttribute(Session session, AttributeKey<T> key) {
- Objects.requireNonNull(key, "No key");
- if (session == null) {
- return null;
- }
-
- T value = session.getAttribute(key);
- return (value != null) ? value : resolveAttribute(session.getFactoryManager(), key);
- }
-
- /**
- * Attempts to use the channel attribute, if not found then tries the session
- *
- * @param <T> The generic attribute type
- * @param channel The {@link Channel} - ignored if {@code null}
- * @param key The attribute key - never {@code null}
- * @return Associated value - {@code null} if not found
- * @see Session#getFactoryManager()
- * @see #resolveAttribute(Session, AttributeKey)
- */
- static <T> T resolveAttribute(Channel channel, AttributeKey<T> key) {
- Objects.requireNonNull(key, "No key");
- if (channel == null) {
- return null;
- }
-
- T value = channel.getAttribute(key);
- return (value != null) ? value : resolveAttribute(channel.getSession(), key);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/BuiltinFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/BuiltinFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/BuiltinFactory.java
deleted file mode 100644
index 33b53a9..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/BuiltinFactory.java
+++ /dev/null
@@ -1,40 +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;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * A named optional factory.
- *
- * @param <T> The create object instance type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface BuiltinFactory<T> extends NamedFactory<T>, OptionalFeature {
- static <T, E extends BuiltinFactory<T>> List<NamedFactory<T>> setUpFactories(
- boolean ignoreUnsupported, Collection<? extends E> preferred) {
- return GenericUtils.stream(preferred)
- .filter(f -> ignoreUnsupported || f.isSupported())
- .collect(Collectors.toList());
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/Closeable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/Closeable.java b/sshd-core/src/main/java/org/apache/sshd/common/Closeable.java
deleted file mode 100644
index 6a6f622..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/Closeable.java
+++ /dev/null
@@ -1,126 +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;
-
-import java.io.IOException;
-import java.net.SocketTimeoutException;
-import java.nio.channels.Channel;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.sshd.common.future.CloseFuture;
-import org.apache.sshd.common.future.SshFutureListener;
-
-/**
- * A {@code Closeable} is a resource that can be closed.
- * The close method is invoked to release resources that the object is
- * holding. The user can pre-register listeners to be notified
- * when resource close is completed (successfully or otherwise)
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Closeable extends Channel {
-
- /**
- * Timeout (milliseconds) for waiting on a {@link CloseFuture} to successfully
- * complete its action.
- * @see #DEFAULT_CLOSE_WAIT_TIMEOUT
- */
- String CLOSE_WAIT_TIMEOUT = "sshd-close-wait-time";
-
- /**
- * Default value for {@link #CLOSE_WAIT_TIMEOUT} if none specified
- */
- long DEFAULT_CLOSE_WAIT_TIMEOUT = TimeUnit.SECONDS.toMillis(15L);
-
- /**
- * Close this resource asynchronously and return a future.
- * Resources support two closing modes: a graceful mode
- * which will cleanly close the resource and an immediate mode
- * which will close the resources abruptly.
- *
- * @param immediately <code>true</code> if the resource should be shut down abruptly,
- * <code>false</code> for a graceful close
- * @return a {@link CloseFuture} representing the close request
- */
- CloseFuture close(boolean immediately);
-
- /**
- * Pre-register a listener to be informed when resource is closed. If
- * resource is already closed, the listener will be invoked immediately
- * and not registered for future notification
- *
- * @param listener The notification {@link SshFutureListener} - never {@code null}
- */
- void addCloseFutureListener(SshFutureListener<CloseFuture> listener);
-
- /**
- * Remove a pre-registered close event listener
- *
- * @param listener The register {@link SshFutureListener} - never {@code null}.
- * Ignored if not registered or resource already closed
- */
- void removeCloseFutureListener(SshFutureListener<CloseFuture> listener);
-
- /**
- * Returns <code>true</code> if this object has been closed.
- *
- * @return <code>true</code> if closing
- */
- boolean isClosed();
-
- /**
- * Returns <code>true</code> if the {@link #close(boolean)} method
- * has been called. Note that this method will return <code>true</code>
- * even if this {@link #isClosed()} returns <code>true</code>.
- *
- * @return <code>true</code> if closing
- */
- boolean isClosing();
-
- @Override
- default boolean isOpen() {
- return !(isClosed() || isClosing());
- }
-
- @Override
- default void close() throws IOException {
- Closeable.close(this);
- }
-
- static long getMaxCloseWaitTime(PropertyResolver resolver) {
- return (resolver == null) ? DEFAULT_CLOSE_WAIT_TIMEOUT
- : resolver.getLongProperty(CLOSE_WAIT_TIMEOUT, DEFAULT_CLOSE_WAIT_TIMEOUT);
- }
-
- static void close(Closeable closeable) throws IOException {
- if (closeable == null) {
- return;
- }
-
- if ((!closeable.isClosed()) && (!closeable.isClosing())) {
- CloseFuture future = closeable.close(true);
- long maxWait = (closeable instanceof PropertyResolver)
- ? getMaxCloseWaitTime((PropertyResolver) closeable) : DEFAULT_CLOSE_WAIT_TIMEOUT;
- boolean successful = future.await(maxWait);
- if (!successful) {
- throw new SocketTimeoutException("Failed to receive closure confirmation within " + maxWait + " millis");
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/Factory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/Factory.java b/sshd-core/src/main/java/org/apache/sshd/common/Factory.java
deleted file mode 100644
index 93b351c..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/Factory.java
+++ /dev/null
@@ -1,41 +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;
-
-import java.util.function.Supplier;
-
-/**
- * Factory is a simple interface that is used to create other objects.
- *
- * @param <T> type of object this factory will create
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface Factory<T> extends Supplier<T> {
-
- @Override
- default T get() {
- return create();
- }
-
- /**
- * @return A new instance
- */
- T create();
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
index 31f0de6..22c7d49 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
@@ -19,6 +19,7 @@
package org.apache.sshd.common;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -467,4 +468,19 @@ public interface FactoryManager
*/
List<RequestHandler<ConnectionService>> getGlobalRequestHandlers();
+ @Override
+ default <T> T resolveAttribute(AttributeKey<T> key) {
+ return resolveAttribute(this, key);
+ }
+
+ /**
+ * @param <T> The generic attribute type
+ * @param manager The {@link FactoryManager} - ignored if {@code null}
+ * @param key The attribute key - never {@code null}
+ * @return Associated value - {@code null} if not found
+ */
+ static <T> T resolveAttribute(FactoryManager manager, AttributeKey<T> key) {
+ Objects.requireNonNull(key, "No key");
+ return (manager == null) ? null : manager.getAttribute(key);
+ }
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
deleted file mode 100644
index 5386447..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/NamedFactory.java
+++ /dev/null
@@ -1,66 +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;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-/**
- * A named factory is a factory identified by a name.
- * Such names are used mainly in the algorithm negotiation at the beginning of the SSH connection.
- *
- * @param <T> The create object instance type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface NamedFactory<T> extends Factory<T>, NamedResource {
- /**
- * Create an instance of the specified name by looking up the needed factory
- * in the list.
- *
- * @param factories list of available factories
- * @param name the factory name to use
- * @param <T> type of object to create
- * @return a newly created object or {@code null} if the factory is not in the list
- */
- static <T> T create(Collection<? extends NamedFactory<? extends T>> factories, String name) {
- NamedFactory<? extends T> f = NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, factories);
- if (f != null) {
- return f.create();
- } else {
- return null;
- }
- }
-
- static <S extends OptionalFeature, T, E extends NamedFactory<T>> List<NamedFactory<T>> setUpTransformedFactories(
- boolean ignoreUnsupported, Collection<? extends S> preferred, Function<? super S, ? extends E> xform) {
- return preferred.stream()
- .filter(f -> ignoreUnsupported || f.isSupported())
- .map(xform)
- .collect(Collectors.toList());
- }
-
- static <T, E extends NamedFactory<T> & OptionalFeature> List<NamedFactory<T>> setUpBuiltinFactories(
- boolean ignoreUnsupported, Collection<? extends E> preferred) {
- return preferred.stream()
- .filter(f -> ignoreUnsupported || f.isSupported())
- .collect(Collectors.toList());
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java b/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
deleted file mode 100644
index 813f53d..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/NamedResource.java
+++ /dev/null
@@ -1,104 +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;
-
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.List;
-import java.util.function.Function;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface NamedResource {
-
- /**
- * Returns the value of {@link #getName()} - or {@code null} if argument is {@code null}
- */
- Function<NamedResource, String> NAME_EXTRACTOR = input -> input == null ? null : input.getName();
-
- /**
- * Compares 2 {@link NamedResource}s according to their {@link #getName()}
- * value case <U>insensitive</U>
- */
- Comparator<NamedResource> BY_NAME_COMPARATOR = Comparator.comparing(NAME_EXTRACTOR, String.CASE_INSENSITIVE_ORDER);
-
- /**
- * @return The resource name
- */
- String getName();
-
- /**
- * @param resources The named resources
- * @return A {@link List} of all the factories names - in same order
- * as they appear in the input collection
- */
- static List<String> getNameList(Collection<? extends NamedResource> resources) {
- return GenericUtils.map(resources, NamedResource::getName);
- }
-
- /**
- * @param resources list of available resources
- * @return A comma separated list of factory names
- */
- static String getNames(Collection<? extends NamedResource> resources) {
- Collection<String> nameList = getNameList(resources);
- return GenericUtils.join(nameList, ',');
- }
-
- /**
- * Remove the resource identified by the name from the list.
- *
- * @param <R> The generic resource type
- * @param name Name of the resource - ignored if {@code null}/empty
- * @param c The {@link Comparator} to decide whether the {@link NamedResource#getName()}
- * matches the <tt>name</tt> parameter
- * @param resources The {@link NamedResource} to check - ignored if {@code null}/empty
- * @return the removed resource from the list or {@code null} if not in the list
- */
- static <R extends NamedResource> R removeByName(String name, Comparator<? super String> c, Collection<? extends R> resources) {
- R r = findByName(name, c, resources);
- if (r != null) {
- resources.remove(r);
- }
- return r;
- }
-
- /**
- * @param <R> The generic resource type
- * @param name Name of the resource - ignored if {@code null}/empty
- * @param c The {@link Comparator} to decide whether the {@link NamedResource#getName()}
- * matches the <tt>name</tt> parameter
- * @param resources The {@link NamedResource} to check - ignored if {@code null}/empty
- * @return The <U>first</U> resource whose name matches the parameter (by invoking
- * {@link Comparator#compare(Object, Object)} - {@code null} if no match found
- */
- static <R extends NamedResource> R findByName(String name, Comparator<? super String> c, Collection<? extends R> resources) {
- return GenericUtils.isEmpty(name)
- ? null
- : GenericUtils.stream(resources)
- .filter(r -> c.compare(name, r.getName()) == 0)
- .findFirst()
- .orElse(null);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/OptionalFeature.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/OptionalFeature.java b/sshd-core/src/main/java/org/apache/sshd/common/OptionalFeature.java
deleted file mode 100644
index 1afa864..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/OptionalFeature.java
+++ /dev/null
@@ -1,92 +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;
-
-import java.util.Collection;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface OptionalFeature {
- OptionalFeature TRUE = new OptionalFeature() {
- @Override
- public boolean isSupported() {
- return true;
- }
-
- @Override
- public String toString() {
- return "TRUE";
- }
- };
-
- OptionalFeature FALSE = new OptionalFeature() {
- @Override
- public boolean isSupported() {
- return false;
- }
-
- @Override
- public String toString() {
- return "FALSE";
- }
- };
-
- boolean isSupported();
-
- static OptionalFeature of(boolean supported) {
- return supported ? TRUE : FALSE;
- }
-
- static OptionalFeature all(Collection<? extends OptionalFeature> features) {
- return () -> {
- if (GenericUtils.isEmpty(features)) {
- return false;
- }
-
- for (OptionalFeature f : features) {
- if (!f.isSupported()) {
- return false;
- }
- }
-
- return true;
- };
- }
-
- static OptionalFeature any(Collection<? extends OptionalFeature> features) {
- return () -> {
- if (GenericUtils.isEmpty(features)) {
- return false;
- }
-
- for (OptionalFeature f : features) {
- if (f.isSupported()) {
- return true;
- }
- }
-
- return false;
- };
- }
-}
[49/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java
new file mode 100644
index 0000000..28a3d1f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java
@@ -0,0 +1,170 @@
+/*
+ * 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.client.config.hosts;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Objects;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.RuntimeSshException;
+import org.apache.sshd.common.mac.Mac;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class KnownHostHashValue {
+ /**
+ * Character used to indicate a hashed host pattern
+ */
+ public static final char HASHED_HOST_DELIMITER = '|';
+
+ public static final NamedFactory<Mac> DEFAULT_DIGEST = KnownHostDigest.SHA1;
+
+ private NamedFactory<Mac> digester = DEFAULT_DIGEST;
+ private byte[] saltValue;
+ private byte[] digestValue;
+
+ public KnownHostHashValue() {
+ super();
+ }
+
+ public NamedFactory<Mac> getDigester() {
+ return digester;
+ }
+
+ public void setDigester(NamedFactory<Mac> digester) {
+ this.digester = digester;
+ }
+
+ public byte[] getSaltValue() {
+ return saltValue;
+ }
+
+ public void setSaltValue(byte[] saltValue) {
+ this.saltValue = saltValue;
+ }
+
+ public byte[] getDigestValue() {
+ return digestValue;
+ }
+
+ public void setDigestValue(byte[] digestValue) {
+ this.digestValue = digestValue;
+ }
+
+ /**
+ * Checks if the host matches the hash
+ *
+ * @param host The host name/address - ignored if {@code null}/empty
+ * @return {@code true} if host matches the hash
+ * @throws RuntimeException If entry not properly initialized
+ */
+ public boolean isHostMatch(String host) {
+ if (GenericUtils.isEmpty(host)) {
+ return false;
+ }
+
+ try {
+ byte[] expected = getDigestValue();
+ byte[] actual = calculateHashValue(host, getDigester(), getSaltValue());
+ return Arrays.equals(expected, actual);
+ } catch (Throwable t) {
+ if (t instanceof RuntimeException) {
+ throw (RuntimeException) t;
+ }
+ throw new RuntimeSshException("Failed (" + t.getClass().getSimpleName() + ")"
+ + " to calculate hash value: " + t.getMessage(), t);
+ }
+ }
+
+ @Override
+ public String toString() {
+ if ((getDigester() == null) || NumberUtils.isEmpty(getSaltValue()) || NumberUtils.isEmpty(getDigestValue())) {
+ return Objects.toString(getDigester(), null)
+ + "-" + BufferUtils.toHex(':', getSaltValue())
+ + "-" + BufferUtils.toHex(':', getDigestValue());
+ }
+
+ try {
+ return append(new StringBuilder(Byte.MAX_VALUE), this).toString();
+ } catch (IOException | RuntimeException e) { // unexpected
+ return e.getClass().getSimpleName() + ": " + e.getMessage();
+ }
+ }
+
+ // see http://nms.lcs.mit.edu/projects/ssh/README.hashed-hosts
+ public static byte[] calculateHashValue(String host, Factory<? extends Mac> factory, byte[] salt) throws Exception {
+ return calculateHashValue(host, factory.create(), salt);
+ }
+
+ public static byte[] calculateHashValue(String host, Mac mac, byte[] salt) throws Exception {
+ mac.init(salt);
+
+ byte[] hostBytes = host.getBytes(StandardCharsets.UTF_8);
+ mac.update(hostBytes);
+ return mac.doFinal();
+ }
+
+ public static <A extends Appendable> A append(A sb, KnownHostHashValue hashValue) throws IOException {
+ return (hashValue == null) ? sb : append(sb, hashValue.getDigester(), hashValue.getSaltValue(), hashValue.getDigestValue());
+ }
+
+ public static <A extends Appendable> A append(A sb, NamedResource factory, byte[] salt, byte[] digest) throws IOException {
+ Base64.Encoder encoder = Base64.getEncoder();
+ sb.append(HASHED_HOST_DELIMITER).append(factory.getName());
+ sb.append(HASHED_HOST_DELIMITER).append(encoder.encodeToString(salt));
+ sb.append(HASHED_HOST_DELIMITER).append(encoder.encodeToString(digest));
+ return sb;
+ }
+
+ public static KnownHostHashValue parse(String patternString) {
+ String pattern = GenericUtils.replaceWhitespaceAndTrim(patternString);
+ return parse(pattern, GenericUtils.isEmpty(pattern) ? null : new KnownHostHashValue());
+ }
+
+ public static <V extends KnownHostHashValue> V parse(String patternString, V value) {
+ String pattern = GenericUtils.replaceWhitespaceAndTrim(patternString);
+ if (GenericUtils.isEmpty(pattern)) {
+ return value;
+ }
+
+ String[] components = GenericUtils.split(pattern, HASHED_HOST_DELIMITER);
+ ValidateUtils.checkTrue(components.length == 4 /* 1st one is empty */, "Invalid hash pattern (insufficient data): %s", pattern);
+ ValidateUtils.checkTrue(GenericUtils.isEmpty(components[0]), "Invalid hash pattern (unexpected extra data): %s", pattern);
+
+ NamedFactory<Mac> factory =
+ ValidateUtils.checkNotNull(KnownHostDigest.fromName(components[1]),
+ "Invalid hash pattern (unknown digest): %s", pattern);
+ Base64.Decoder decoder = Base64.getDecoder();
+ value.setDigester(factory);
+ value.setSaltValue(decoder.decode(components[2]));
+ value.setDigestValue(decoder.decode(components[3]));
+ return value;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
new file mode 100644
index 0000000..ab9929b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcher.java
@@ -0,0 +1,106 @@
+/*
+ * 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.client.config.keys;
+
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.keys.BuiltinIdentities;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BuiltinClientIdentitiesWatcher extends ClientIdentitiesWatcher {
+ private final boolean supportedOnly;
+
+ public BuiltinClientIdentitiesWatcher(Path keysFolder, boolean supportedOnly,
+ ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+ this(keysFolder, NamedResource.getNameList(BuiltinIdentities.VALUES), supportedOnly, loader, provider, strict);
+ }
+
+ public BuiltinClientIdentitiesWatcher(Path keysFolder, Collection<String> ids, boolean supportedOnly,
+ ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+ this(keysFolder, ids, supportedOnly,
+ GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
+ GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
+ strict);
+ }
+
+ public BuiltinClientIdentitiesWatcher(Path keysFolder, boolean supportedOnly,
+ Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+ this(keysFolder, NamedResource.getNameList(BuiltinIdentities.VALUES), supportedOnly, loader, provider, strict);
+ }
+
+ public BuiltinClientIdentitiesWatcher(Path keysFolder, Collection<String> ids, boolean supportedOnly,
+ Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+ super(getBuiltinIdentitiesPaths(keysFolder, ids), loader, provider, strict);
+ this.supportedOnly = supportedOnly;
+ }
+
+ public final boolean isSupportedOnly() {
+ return supportedOnly;
+ }
+
+ @Override
+ public Iterable<KeyPair> loadKeys() {
+ return isSupportedOnly() ? loadKeys(this::isSupported) : super.loadKeys();
+ }
+
+ private boolean isSupported(KeyPair kp) {
+ BuiltinIdentities id = BuiltinIdentities.fromKeyPair(kp);
+ if ((id != null) && id.isSupported()) {
+ return true;
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("loadKeys - remove unsupported identity={}, key-type={}, key={}",
+ id, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
+ }
+ return false;
+ }
+
+ public static List<Path> getDefaultBuiltinIdentitiesPaths(Path keysFolder) {
+ return getBuiltinIdentitiesPaths(keysFolder, NamedResource.getNameList(BuiltinIdentities.VALUES));
+ }
+
+ public static List<Path> getBuiltinIdentitiesPaths(Path keysFolder, Collection<String> ids) {
+ Objects.requireNonNull(keysFolder, "No keys folder");
+ if (GenericUtils.isEmpty(ids)) {
+ return Collections.emptyList();
+ }
+
+ List<Path> paths = new ArrayList<>(ids.size());
+ for (String id : ids) {
+ String fileName = ClientIdentity.getIdentityFileName(id);
+ paths.add(keysFolder.resolve(fileName));
+ }
+
+ return paths;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
new file mode 100644
index 0000000..094766f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentitiesWatcher.java
@@ -0,0 +1,139 @@
+/*
+ * 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.client.config.keys;
+
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * Watches over a group of files that contains client identities
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ClientIdentitiesWatcher extends AbstractKeyPairProvider implements KeyPairProvider {
+ private final Collection<ClientIdentityProvider> providers;
+
+ public ClientIdentitiesWatcher(Collection<? extends Path> paths,
+ ClientIdentityLoader loader, FilePasswordProvider provider) {
+ this(paths, loader, provider, true);
+ }
+
+ public ClientIdentitiesWatcher(Collection<? extends Path> paths,
+ ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+ this(paths,
+ GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
+ GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
+ strict);
+ }
+
+ public ClientIdentitiesWatcher(Collection<? extends Path> paths,
+ Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
+ this(paths, loader, provider, true);
+ }
+
+ public ClientIdentitiesWatcher(Collection<? extends Path> paths,
+ Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+ this(buildProviders(paths, loader, provider, strict));
+ }
+
+ public ClientIdentitiesWatcher(Collection<ClientIdentityProvider> providers) {
+ this.providers = providers;
+ }
+
+ @Override
+ public Iterable<KeyPair> loadKeys() {
+ return loadKeys(null);
+ }
+
+ protected Iterable<KeyPair> loadKeys(Predicate<? super KeyPair> filter) {
+ return () -> {
+ Stream<KeyPair> stream = safeMap(GenericUtils.stream(providers), this::doGetKeyPair);
+ if (filter != null) {
+ stream = stream.filter(filter);
+ }
+ return stream.iterator();
+ };
+ }
+
+ /**
+ * Performs a mapping operation on the stream, discarding any null values
+ * returned by the mapper.
+ *
+ * @param <U> Original type
+ * @param <V> Mapped type
+ * @param stream Original values stream
+ * @param mapper Mapper to target type
+ * @return Mapped stream
+ */
+ protected <U, V> Stream<V> safeMap(Stream<U> stream, Function<? super U, ? extends V> mapper) {
+ return stream.map(u -> Optional.ofNullable(mapper.apply(u)))
+ .filter(Optional::isPresent)
+ .map(Optional::get);
+ }
+
+ protected KeyPair doGetKeyPair(ClientIdentityProvider p) {
+ try {
+ KeyPair kp = p.getClientIdentity();
+ if (kp == null) {
+ if (log.isDebugEnabled()) {
+ log.debug("loadKeys({}) no key loaded", p);
+ }
+ }
+ return kp;
+ } catch (Throwable e) {
+ log.warn("loadKeys({}) failed ({}) to load key: {}", p, e.getClass().getSimpleName(), e.getMessage());
+ if (log.isDebugEnabled()) {
+ log.debug("loadKeys(" + p + ") key load failure details", e);
+ }
+ return null;
+ }
+ }
+
+ public static List<ClientIdentityProvider> buildProviders(
+ Collection<? extends Path> paths, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+ return buildProviders(paths,
+ GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
+ GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
+ strict);
+ }
+
+ public static List<ClientIdentityProvider> buildProviders(
+ Collection<? extends Path> paths, Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+ if (GenericUtils.isEmpty(paths)) {
+ return Collections.emptyList();
+ }
+
+ return GenericUtils.map(paths, p -> new ClientIdentityFileWatcher(p, loader, provider, strict));
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
new file mode 100644
index 0000000..931d96f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentity.java
@@ -0,0 +1,268 @@
+/*
+ * 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.client.config.keys;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.function.Function;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.keys.BuiltinIdentities;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.IdentityUtils;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.FileInfoExtractor;
+import org.apache.sshd.common.util.io.IoUtils;
+
+/**
+ * Provides keys loading capability from the user's keys folder - e.g., {@code id_rsa}
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see org.apache.sshd.common.util.security.SecurityUtils#getKeyPairResourceParser()
+ */
+public final class ClientIdentity {
+
+ public static final String ID_FILE_PREFIX = "id_";
+
+ public static final String ID_FILE_SUFFIX = "";
+
+ public static final Function<String, String> ID_GENERATOR =
+ ClientIdentity::getIdentityFileName;
+
+ private ClientIdentity() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ /**
+ * @param name The file name - ignored if {@code null}/empty
+ * @return The identity type - {@code null} if cannot determine it - e.g.,
+ * does not start with the {@link #ID_FILE_PREFIX}
+ */
+ public static String getIdentityType(String name) {
+ if (GenericUtils.isEmpty(name)
+ || (name.length() <= ID_FILE_PREFIX.length())
+ || (!name.startsWith(ID_FILE_PREFIX))) {
+ return null;
+ } else {
+ return name.substring(ID_FILE_PREFIX.length());
+ }
+ }
+
+ public static String getIdentityFileName(NamedResource r) {
+ return getIdentityFileName((r == null) ? null : r.getName());
+ }
+
+ /**
+ * @param type The identity type - e.g., {@code rsa} - ignored
+ * if {@code null}/empty
+ * @return The matching file name for the identity - {@code null}
+ * if no name
+ * @see #ID_FILE_PREFIX
+ * @see #ID_FILE_SUFFIX
+ * @see IdentityUtils#getIdentityFileName(String, String, String)
+ */
+ public static String getIdentityFileName(String type) {
+ return IdentityUtils.getIdentityFileName(ID_FILE_PREFIX, type, ID_FILE_SUFFIX);
+ }
+
+ /**
+ * @param strict If {@code true} then files that do not have the required
+ * access rights are excluded from consideration
+ * @param supportedOnly If {@code true} then ignore identities that are not
+ * supported internally
+ * @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 LinkOption}s to apply when checking
+ * for existence
+ * @return A {@link KeyPair} for the identities - {@code null} if no identities
+ * available (e.g., after filtering unsupported ones or strict permissions)
+ * @throws IOException If failed to access the file system
+ * @throws GeneralSecurityException If failed to load the keys
+ * @see PublicKeyEntry#getDefaultKeysFolderPath()
+ * @see #loadDefaultIdentities(Path, boolean, FilePasswordProvider, LinkOption...)
+ */
+ public static KeyPairProvider loadDefaultKeyPairProvider(
+ boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
+ throws IOException, GeneralSecurityException {
+ return loadDefaultKeyPairProvider(PublicKeyEntry.getDefaultKeysFolderPath(), strict, supportedOnly, provider, options);
+ }
+
+ /**
+ * @param dir The folder to scan for the built-in identities
+ * @param strict If {@code true} then files that do not have the required
+ * access rights are excluded from consideration
+ * @param supportedOnly If {@code true} then ignore identities that are not
+ * supported internally
+ * @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 LinkOption}s to apply when checking
+ * for existence
+ * @return A {@link KeyPair} for the identities - {@code null} if no identities
+ * available (e.g., after filtering unsupported ones or strict permissions)
+ * @throws IOException If failed to access the file system
+ * @throws GeneralSecurityException If failed to load the keys
+ * @see #loadDefaultIdentities(Path, boolean, FilePasswordProvider, LinkOption...)
+ * @see IdentityUtils#createKeyPairProvider(Map, boolean)
+ */
+ public static KeyPairProvider loadDefaultKeyPairProvider(
+ Path dir, boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
+ throws IOException, GeneralSecurityException {
+ Map<String, KeyPair> ids = loadDefaultIdentities(dir, strict, provider, options);
+ return IdentityUtils.createKeyPairProvider(ids, supportedOnly);
+ }
+
+ /**
+ * @param strict If {@code true} then files that do not have the required
+ * access rights are excluded from consideration
+ * @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 LinkOption}s to apply when checking
+ * for existence
+ * @return A {@link Map} of the found files 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 PublicKeyEntry#getDefaultKeysFolderPath()
+ * @see #loadDefaultIdentities(Path, boolean, FilePasswordProvider, LinkOption...)
+ */
+ public static Map<String, KeyPair> loadDefaultIdentities(boolean strict, FilePasswordProvider provider, LinkOption... options)
+ throws IOException, GeneralSecurityException {
+ return loadDefaultIdentities(PublicKeyEntry.getDefaultKeysFolderPath(), strict, provider, options);
+ }
+
+ /**
+ * @param dir The folder to scan for the built-in identities
+ * @param strict If {@code true} then files that do not have the required
+ * access rights are excluded from consideration
+ * @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 LinkOption}s to apply when checking
+ * for existence
+ * @return A {@link Map} of the found files 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 #loadIdentities(Path, boolean, Collection, Function, FilePasswordProvider, LinkOption...)
+ * @see BuiltinIdentities
+ */
+ public static Map<String, KeyPair> loadDefaultIdentities(Path dir, boolean strict, FilePasswordProvider provider, LinkOption... options)
+ throws IOException, GeneralSecurityException {
+ return loadIdentities(dir, strict, BuiltinIdentities.NAMES, ID_GENERATOR, provider, options);
+ }
+
+ /**
+ * Scans a folder and loads all available identity files
+ *
+ * @param dir The {@link Path} of the folder to scan - ignored if not exists
+ * @param strict If {@code true} then files that do not have the required
+ * access rights are excluded from consideration
+ * @param types The identity types - ignored if {@code null}/empty
+ * @param idGenerator A {@link Function} to derive the file name
+ * holding the specified type
+ * @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 LinkOption}s to apply when checking
+ * for existence
+ * @return A {@link Map} of the found files 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 #scanIdentitiesFolder(Path, boolean, Collection, Function, LinkOption...)
+ * @see IdentityUtils#loadIdentities(Map, FilePasswordProvider, java.nio.file.OpenOption...)
+ */
+ public static Map<String, KeyPair> loadIdentities(
+ Path dir, boolean strict, Collection<String> types, Function<String, String> idGenerator, FilePasswordProvider provider, LinkOption... options)
+ throws IOException, GeneralSecurityException {
+ Map<String, Path> paths = scanIdentitiesFolder(dir, strict, types, idGenerator, options);
+ return IdentityUtils.loadIdentities(paths, provider, IoUtils.EMPTY_OPEN_OPTIONS);
+ }
+
+ /**
+ * Scans a folder for possible identity files
+ *
+ * @param dir The {@link Path} of the folder to scan - ignored if not exists
+ * @param strict If {@code true} then files that do not have the required
+ * access rights are excluded from consideration
+ * @param types The identity types - ignored if {@code null}/empty
+ * @param idGenerator A {@link Function} to derive the file name
+ * holding the specified type
+ * @param options The {@link LinkOption}s to apply when checking
+ * for existence
+ * @return A {@link Map} of the found files where key=identity type (case
+ * <U>insensitive</U>), value=the {@link Path} of the file holding the key
+ * @throws IOException If failed to access the file system
+ * @see KeyUtils#validateStrictKeyFilePermissions(Path, LinkOption...)
+ */
+ public static Map<String, Path> scanIdentitiesFolder(
+ Path dir, boolean strict, Collection<String> types, Function<String, String> idGenerator, LinkOption... options)
+ throws IOException {
+ if (GenericUtils.isEmpty(types)) {
+ return Collections.emptyMap();
+ }
+
+ if (!Files.exists(dir, options)) {
+ return Collections.emptyMap();
+ }
+
+ ValidateUtils.checkTrue(FileInfoExtractor.ISDIR.infoOf(dir, options), "Not a directory: %s", dir);
+
+ Map<String, Path> paths = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ for (String t : types) {
+ String fileName = idGenerator.apply(t);
+ Path p = dir.resolve(fileName);
+ if (!Files.exists(p, options)) {
+ continue;
+ }
+
+ if (strict) {
+ if (KeyUtils.validateStrictKeyFilePermissions(p, options) != null) {
+ continue;
+ }
+ }
+
+ Path prev = paths.put(t, p);
+ ValidateUtils.checkTrue(prev == null, "Multiple mappings for type=%s", t);
+ }
+
+ return paths;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
new file mode 100644
index 0000000..a00cb24
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcher.java
@@ -0,0 +1,141 @@
+/*
+ * 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.client.config.keys;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.io.ModifiableFileWatcher;
+
+/**
+ * A {@link ClientIdentityProvider} that watches a given key file re-loading
+ * its contents if it is ever modified, deleted or (re-)created
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class ClientIdentityFileWatcher extends ModifiableFileWatcher implements ClientIdentityProvider {
+ private final AtomicReference<KeyPair> identityHolder = new AtomicReference<>(null);
+ private final Supplier<ClientIdentityLoader> loaderHolder;
+ private final Supplier<FilePasswordProvider> providerHolder;
+ private final boolean strict;
+
+ public ClientIdentityFileWatcher(Path path, ClientIdentityLoader loader, FilePasswordProvider provider) {
+ this(path, loader, provider, true);
+ }
+
+ public ClientIdentityFileWatcher(Path path, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+ this(path,
+ GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
+ GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
+ strict);
+ }
+
+ public ClientIdentityFileWatcher(Path path, Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
+ this(path, loader, provider, true);
+ }
+
+ public ClientIdentityFileWatcher(Path path, Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+ super(path);
+ this.loaderHolder = Objects.requireNonNull(loader, "No client identity loader");
+ this.providerHolder = Objects.requireNonNull(provider, "No password provider");
+ this.strict = strict;
+ }
+
+ public final boolean isStrict() {
+ return strict;
+ }
+
+ public final ClientIdentityLoader getClientIdentityLoader() {
+ return loaderHolder.get();
+ }
+
+ public final FilePasswordProvider getFilePasswordProvider() {
+ return providerHolder.get();
+ }
+
+ @Override
+ public KeyPair getClientIdentity() throws IOException, GeneralSecurityException {
+ if (checkReloadRequired()) {
+ KeyPair kp = identityHolder.getAndSet(null); // start fresh
+ Path path = getPath();
+
+ if (exists()) {
+ KeyPair id = reloadClientIdentity(path);
+ if (!KeyUtils.compareKeyPairs(kp, id)) {
+ if (log.isDebugEnabled()) {
+ log.debug("getClientIdentity({}) identity {}", path, (kp == null) ? "loaded" : "re-loaded");
+ }
+ }
+
+ updateReloadAttributes();
+ identityHolder.set(id);
+ }
+ }
+
+ return identityHolder.get();
+ }
+
+ protected KeyPair reloadClientIdentity(Path path) throws IOException, GeneralSecurityException {
+ if (isStrict()) {
+ Map.Entry<String, Object> violation = KeyUtils.validateStrictKeyFilePermissions(path, IoUtils.EMPTY_LINK_OPTIONS);
+ if (violation != null) {
+ if (log.isDebugEnabled()) {
+ log.debug("reloadClientIdentity({}) ignore due to {}", path, violation.getKey());
+ }
+ return null;
+ }
+ }
+
+ String location = path.toString();
+ ClientIdentityLoader idLoader = Objects.requireNonNull(getClientIdentityLoader(), "No client identity loader");
+ if (idLoader.isValidLocation(location)) {
+ KeyPair kp = idLoader.loadClientIdentity(location, Objects.requireNonNull(getFilePasswordProvider(), "No file password provider"));
+ if (log.isTraceEnabled()) {
+ PublicKey key = (kp == null) ? null : kp.getPublic();
+ if (key != null) {
+ log.trace("reloadClientIdentity({}) loaded {}-{}",
+ location, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+
+ } else {
+ log.trace("reloadClientIdentity({}) no key loaded", location);
+ }
+ }
+
+ return kp;
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("reloadClientIdentity({}) invalid location", location);
+ }
+
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java
new file mode 100644
index 0000000..8b3a295
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityLoader.java
@@ -0,0 +1,95 @@
+/*
+ * 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.client.config.keys;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface ClientIdentityLoader {
+ /**
+ * <P>A default implementation that assumes a file location that <U>must</U> exist.</P>
+ *
+ * <P>
+ * <B>Note:</B> It calls {@link SecurityUtils#loadKeyPairIdentity(String, InputStream, FilePasswordProvider)}
+ * </P>
+ */
+ ClientIdentityLoader DEFAULT = new ClientIdentityLoader() {
+ @Override
+ public boolean isValidLocation(String location) throws IOException {
+ Path path = toPath(location);
+ return Files.exists(path, IoUtils.EMPTY_LINK_OPTIONS);
+ }
+
+ @Override
+ public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException {
+ Path path = toPath(location);
+ try (InputStream inputStream = Files.newInputStream(path, IoUtils.EMPTY_OPEN_OPTIONS)) {
+ return SecurityUtils.loadKeyPairIdentity(path.toString(), inputStream, provider);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "DEFAULT";
+ }
+
+ private Path toPath(String location) {
+ Path path = Paths.get(ValidateUtils.checkNotNullAndNotEmpty(location, "No location"));
+ path = path.toAbsolutePath();
+ path = path.normalize();
+ return path;
+ }
+ };
+
+ /**
+ * @param location The identity key-pair location - the actual meaning (file, URL, etc.)
+ * depends on the implementation.
+ * @return {@code true} if it represents a valid location - the actual meaning of
+ * the validity depends on the implementation
+ * @throws IOException If failed to validate the location
+ */
+ boolean isValidLocation(String location) throws IOException;
+
+ /**
+ * @param location The identity key-pair location - the actual meaning (file, URL, etc.)
+ * depends on the implementation.
+ * @param provider The {@link FilePasswordProvider} to consult if the location contains
+ * an encrypted identity
+ * @return The loaded {@link KeyPair} - {@code null} if location is empty
+ * and it is OK that it does not exist
+ * @throws IOException If failed to access / process the remote location
+ * @throws GeneralSecurityException If failed to convert the contents into
+ * a valid identity
+ */
+ KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
new file mode 100644
index 0000000..bda4700
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/ClientIdentityProvider.java
@@ -0,0 +1,42 @@
+/*
+ * 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.client.config.keys;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface ClientIdentityProvider {
+ /**
+ * Provides a {@link KeyPair} representing the client identity
+ *
+ * @return The client identity - may be {@code null} if no currently
+ * available identity from this provider. <B>Note:</B> the provider
+ * may return a <U>different</U> value every time this method is called
+ * - e.g., if it is (re-)loading contents from a file.
+ * @throws IOException If failed to load the identity
+ * @throws GeneralSecurityException If failed to parse the identity
+ */
+ KeyPair getClientIdentity() throws IOException, GeneralSecurityException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
new file mode 100644
index 0000000..3afa129
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/keys/DefaultClientIdentitiesWatcher.java
@@ -0,0 +1,66 @@
+/*
+ * 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.client.config.keys;
+
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DefaultClientIdentitiesWatcher extends BuiltinClientIdentitiesWatcher {
+ public DefaultClientIdentitiesWatcher(ClientIdentityLoader loader, FilePasswordProvider provider) {
+ this(loader, provider, true);
+ }
+
+ public DefaultClientIdentitiesWatcher(ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+ this(true, loader, provider, strict);
+ }
+
+ public DefaultClientIdentitiesWatcher(boolean supportedOnly, ClientIdentityLoader loader, FilePasswordProvider provider, boolean strict) {
+ this(supportedOnly,
+ GenericUtils.supplierOf(Objects.requireNonNull(loader, "No client identity loader")),
+ GenericUtils.supplierOf(Objects.requireNonNull(provider, "No password provider")),
+ strict);
+ }
+
+ public DefaultClientIdentitiesWatcher(Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider) {
+ this(loader, provider, true);
+ }
+
+ public DefaultClientIdentitiesWatcher(Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+ this(true, loader, provider, strict);
+ }
+
+ public DefaultClientIdentitiesWatcher(boolean supportedOnly,
+ Supplier<ClientIdentityLoader> loader, Supplier<FilePasswordProvider> provider, boolean strict) {
+ super(PublicKeyEntry.getDefaultKeysFolderPath(), supportedOnly, loader, provider, strict);
+ }
+
+ public static List<Path> getDefaultBuiltinIdentitiesPaths() {
+ return getDefaultBuiltinIdentitiesPaths(PublicKeyEntry.getDefaultKeysFolderPath());
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java b/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java
new file mode 100644
index 0000000..96f6ab1
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java
@@ -0,0 +1,97 @@
+/*
+ * 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;
+
+/**
+ * Provides the capability to attach in-memory attributes to the entity
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface AttributeStore {
+ /**
+ * <P>
+ * Type safe key for storage of user attributes. Typically it is used as a static
+ * variable that is shared between the producer and the consumer. To further
+ * restrict access the setting or getting it from the store one can add static
+ * {@code get/set methods} e.g:
+ * </P>
+ *
+ * <pre>
+ * public static final AttributeKey<MyValue> MY_KEY = new AttributeKey<MyValue>();
+ *
+ * public static MyValue getMyValue(Session s) {
+ * return s.getAttribute(MY_KEY);
+ * }
+ *
+ * public static void setMyValue(Session s, MyValue value) {
+ * s.setAttribute(MY_KEY, value);
+ * }
+ * </pre>
+ *
+ * @param <T> type of value stored in the attribute.
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+ // CHECKSTYLE:OFF
+ class AttributeKey<T> {
+ public AttributeKey() {
+ super();
+ }
+ }
+ // CHECKSTYLE:ON
+
+ /**
+ * Returns the value of the user-defined attribute.
+ *
+ * @param <T> The generic attribute type
+ * @param key The key of the attribute; must not be {@code null}.
+ * @return {@code null} if there is no value associated with the specified key
+ */
+ <T> T getAttribute(AttributeKey<T> key);
+
+ /**
+ * Sets a user-defined attribute.
+ *
+ * @param <T> The generic attribute type
+ * @param key The key of the attribute; must not be {@code null}.
+ * @param value The value of the attribute; must not be {@code null}.
+ * @return The old value of the attribute; {@code null} if it is new.
+ */
+ <T> T setAttribute(AttributeKey<T> key, T value);
+
+ /**
+ * Removes the user-defined attribute
+ *
+ * @param <T> The generic attribute type
+ * @param key The key of the attribute; must not be {@code null}.
+ * @return The removed value; {@code null} if no previous value
+ */
+ <T> T removeAttribute(AttributeKey<T> key);
+
+ /**
+ * Attempts to resolve the associated value by going up the store's
+ * hierarchy (if any)
+ *
+ * @param <T> The generic attribute type
+ * @param key The key of the attribute; must not be {@code null}.
+ * @return {@code null} if there is no value associated with the specified key
+ */
+ <T> T resolveAttribute(AttributeKey<T> key);
+}
+
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/BuiltinFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/BuiltinFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/BuiltinFactory.java
new file mode 100644
index 0000000..33b53a9
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/BuiltinFactory.java
@@ -0,0 +1,40 @@
+/*
+ * 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.List;
+import java.util.stream.Collectors;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * A named optional factory.
+ *
+ * @param <T> The create object instance type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface BuiltinFactory<T> extends NamedFactory<T>, OptionalFeature {
+ static <T, E extends BuiltinFactory<T>> List<NamedFactory<T>> setUpFactories(
+ boolean ignoreUnsupported, Collection<? extends E> preferred) {
+ return GenericUtils.stream(preferred)
+ .filter(f -> ignoreUnsupported || f.isSupported())
+ .collect(Collectors.toList());
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/Closeable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/Closeable.java b/sshd-common/src/main/java/org/apache/sshd/common/Closeable.java
new file mode 100644
index 0000000..6a6f622
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/Closeable.java
@@ -0,0 +1,126 @@
+/*
+ * 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.net.SocketTimeoutException;
+import java.nio.channels.Channel;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+
+/**
+ * A {@code Closeable} is a resource that can be closed.
+ * The close method is invoked to release resources that the object is
+ * holding. The user can pre-register listeners to be notified
+ * when resource close is completed (successfully or otherwise)
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface Closeable extends Channel {
+
+ /**
+ * Timeout (milliseconds) for waiting on a {@link CloseFuture} to successfully
+ * complete its action.
+ * @see #DEFAULT_CLOSE_WAIT_TIMEOUT
+ */
+ String CLOSE_WAIT_TIMEOUT = "sshd-close-wait-time";
+
+ /**
+ * Default value for {@link #CLOSE_WAIT_TIMEOUT} if none specified
+ */
+ long DEFAULT_CLOSE_WAIT_TIMEOUT = TimeUnit.SECONDS.toMillis(15L);
+
+ /**
+ * Close this resource asynchronously and return a future.
+ * Resources support two closing modes: a graceful mode
+ * which will cleanly close the resource and an immediate mode
+ * which will close the resources abruptly.
+ *
+ * @param immediately <code>true</code> if the resource should be shut down abruptly,
+ * <code>false</code> for a graceful close
+ * @return a {@link CloseFuture} representing the close request
+ */
+ CloseFuture close(boolean immediately);
+
+ /**
+ * Pre-register a listener to be informed when resource is closed. If
+ * resource is already closed, the listener will be invoked immediately
+ * and not registered for future notification
+ *
+ * @param listener The notification {@link SshFutureListener} - never {@code null}
+ */
+ void addCloseFutureListener(SshFutureListener<CloseFuture> listener);
+
+ /**
+ * Remove a pre-registered close event listener
+ *
+ * @param listener The register {@link SshFutureListener} - never {@code null}.
+ * Ignored if not registered or resource already closed
+ */
+ void removeCloseFutureListener(SshFutureListener<CloseFuture> listener);
+
+ /**
+ * Returns <code>true</code> if this object has been closed.
+ *
+ * @return <code>true</code> if closing
+ */
+ boolean isClosed();
+
+ /**
+ * Returns <code>true</code> if the {@link #close(boolean)} method
+ * has been called. Note that this method will return <code>true</code>
+ * even if this {@link #isClosed()} returns <code>true</code>.
+ *
+ * @return <code>true</code> if closing
+ */
+ boolean isClosing();
+
+ @Override
+ default boolean isOpen() {
+ return !(isClosed() || isClosing());
+ }
+
+ @Override
+ default void close() throws IOException {
+ Closeable.close(this);
+ }
+
+ static long getMaxCloseWaitTime(PropertyResolver resolver) {
+ return (resolver == null) ? DEFAULT_CLOSE_WAIT_TIMEOUT
+ : resolver.getLongProperty(CLOSE_WAIT_TIMEOUT, DEFAULT_CLOSE_WAIT_TIMEOUT);
+ }
+
+ static void close(Closeable closeable) throws IOException {
+ if (closeable == null) {
+ return;
+ }
+
+ if ((!closeable.isClosed()) && (!closeable.isClosing())) {
+ CloseFuture future = closeable.close(true);
+ long maxWait = (closeable instanceof PropertyResolver)
+ ? getMaxCloseWaitTime((PropertyResolver) closeable) : DEFAULT_CLOSE_WAIT_TIMEOUT;
+ boolean successful = future.await(maxWait);
+ if (!successful) {
+ throw new SocketTimeoutException("Failed to receive closure confirmation within " + maxWait + " millis");
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/Factory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/Factory.java b/sshd-common/src/main/java/org/apache/sshd/common/Factory.java
new file mode 100644
index 0000000..93b351c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/Factory.java
@@ -0,0 +1,41 @@
+/*
+ * 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.function.Supplier;
+
+/**
+ * Factory is a simple interface that is used to create other objects.
+ *
+ * @param <T> type of object this factory will create
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface Factory<T> extends Supplier<T> {
+
+ @Override
+ default T get() {
+ return create();
+ }
+
+ /**
+ * @return A new instance
+ */
+ T create();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/NamedFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/NamedFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/NamedFactory.java
new file mode 100644
index 0000000..5386447
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/NamedFactory.java
@@ -0,0 +1,66 @@
+/*
+ * 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.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * A named factory is a factory identified by a name.
+ * Such names are used mainly in the algorithm negotiation at the beginning of the SSH connection.
+ *
+ * @param <T> The create object instance type
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface NamedFactory<T> extends Factory<T>, NamedResource {
+ /**
+ * Create an instance of the specified name by looking up the needed factory
+ * in the list.
+ *
+ * @param factories list of available factories
+ * @param name the factory name to use
+ * @param <T> type of object to create
+ * @return a newly created object or {@code null} if the factory is not in the list
+ */
+ static <T> T create(Collection<? extends NamedFactory<? extends T>> factories, String name) {
+ NamedFactory<? extends T> f = NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, factories);
+ if (f != null) {
+ return f.create();
+ } else {
+ return null;
+ }
+ }
+
+ static <S extends OptionalFeature, T, E extends NamedFactory<T>> List<NamedFactory<T>> setUpTransformedFactories(
+ boolean ignoreUnsupported, Collection<? extends S> preferred, Function<? super S, ? extends E> xform) {
+ return preferred.stream()
+ .filter(f -> ignoreUnsupported || f.isSupported())
+ .map(xform)
+ .collect(Collectors.toList());
+ }
+
+ static <T, E extends NamedFactory<T> & OptionalFeature> List<NamedFactory<T>> setUpBuiltinFactories(
+ boolean ignoreUnsupported, Collection<? extends E> preferred) {
+ return preferred.stream()
+ .filter(f -> ignoreUnsupported || f.isSupported())
+ .collect(Collectors.toList());
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java b/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java
new file mode 100644
index 0000000..813f53d
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java
@@ -0,0 +1,104 @@
+/*
+ * 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.Comparator;
+import java.util.List;
+import java.util.function.Function;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface NamedResource {
+
+ /**
+ * Returns the value of {@link #getName()} - or {@code null} if argument is {@code null}
+ */
+ Function<NamedResource, String> NAME_EXTRACTOR = input -> input == null ? null : input.getName();
+
+ /**
+ * Compares 2 {@link NamedResource}s according to their {@link #getName()}
+ * value case <U>insensitive</U>
+ */
+ Comparator<NamedResource> BY_NAME_COMPARATOR = Comparator.comparing(NAME_EXTRACTOR, String.CASE_INSENSITIVE_ORDER);
+
+ /**
+ * @return The resource name
+ */
+ String getName();
+
+ /**
+ * @param resources The named resources
+ * @return A {@link List} of all the factories names - in same order
+ * as they appear in the input collection
+ */
+ static List<String> getNameList(Collection<? extends NamedResource> resources) {
+ return GenericUtils.map(resources, NamedResource::getName);
+ }
+
+ /**
+ * @param resources list of available resources
+ * @return A comma separated list of factory names
+ */
+ static String getNames(Collection<? extends NamedResource> resources) {
+ Collection<String> nameList = getNameList(resources);
+ return GenericUtils.join(nameList, ',');
+ }
+
+ /**
+ * Remove the resource identified by the name from the list.
+ *
+ * @param <R> The generic resource type
+ * @param name Name of the resource - ignored if {@code null}/empty
+ * @param c The {@link Comparator} to decide whether the {@link NamedResource#getName()}
+ * matches the <tt>name</tt> parameter
+ * @param resources The {@link NamedResource} to check - ignored if {@code null}/empty
+ * @return the removed resource from the list or {@code null} if not in the list
+ */
+ static <R extends NamedResource> R removeByName(String name, Comparator<? super String> c, Collection<? extends R> resources) {
+ R r = findByName(name, c, resources);
+ if (r != null) {
+ resources.remove(r);
+ }
+ return r;
+ }
+
+ /**
+ * @param <R> The generic resource type
+ * @param name Name of the resource - ignored if {@code null}/empty
+ * @param c The {@link Comparator} to decide whether the {@link NamedResource#getName()}
+ * matches the <tt>name</tt> parameter
+ * @param resources The {@link NamedResource} to check - ignored if {@code null}/empty
+ * @return The <U>first</U> resource whose name matches the parameter (by invoking
+ * {@link Comparator#compare(Object, Object)} - {@code null} if no match found
+ */
+ static <R extends NamedResource> R findByName(String name, Comparator<? super String> c, Collection<? extends R> resources) {
+ return GenericUtils.isEmpty(name)
+ ? null
+ : GenericUtils.stream(resources)
+ .filter(r -> c.compare(name, r.getName()) == 0)
+ .findFirst()
+ .orElse(null);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/OptionalFeature.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/OptionalFeature.java b/sshd-common/src/main/java/org/apache/sshd/common/OptionalFeature.java
new file mode 100644
index 0000000..1afa864
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/OptionalFeature.java
@@ -0,0 +1,92 @@
+/*
+ * 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 org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface OptionalFeature {
+ OptionalFeature TRUE = new OptionalFeature() {
+ @Override
+ public boolean isSupported() {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "TRUE";
+ }
+ };
+
+ OptionalFeature FALSE = new OptionalFeature() {
+ @Override
+ public boolean isSupported() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "FALSE";
+ }
+ };
+
+ boolean isSupported();
+
+ static OptionalFeature of(boolean supported) {
+ return supported ? TRUE : FALSE;
+ }
+
+ static OptionalFeature all(Collection<? extends OptionalFeature> features) {
+ return () -> {
+ if (GenericUtils.isEmpty(features)) {
+ return false;
+ }
+
+ for (OptionalFeature f : features) {
+ if (!f.isSupported()) {
+ return false;
+ }
+ }
+
+ return true;
+ };
+ }
+
+ static OptionalFeature any(Collection<? extends OptionalFeature> features) {
+ return () -> {
+ if (GenericUtils.isEmpty(features)) {
+ return false;
+ }
+
+ for (OptionalFeature f : features) {
+ if (f.isSupported()) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolver.java b/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolver.java
new file mode 100644
index 0000000..c333c7f
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolver.java
@@ -0,0 +1,124 @@
+/*
+ * 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.Collections;
+import java.util.Map;
+
+/**
+ * Indicates an entity that can be configured using properties. The properties
+ * are simple name-value pairs where the actual value type depends on the
+ * property. Some automatic conversions may be available - e.g., from a string
+ * to a numeric or {@code boolean} value, or from {@code int} to {@code long},
+ * etc.. <B>Note:</B> implementations may decide to use case <U>insensitive</U>
+ * property names, therefore it is <U><B>highly discouraged</B></U> to use names
+ * that differ from each other only in case sensitivity. Also, implementations
+ * may choose to trim whitespaces, thus such are also highly discouraged.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface PropertyResolver {
+ /**
+ * An "empty" resolver with no properties and no parent
+ */
+ PropertyResolver EMPTY = new PropertyResolver() {
+ @Override
+ public PropertyResolver getParentPropertyResolver() {
+ return null;
+ }
+
+ @Override
+ public Map<String, Object> getProperties() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public String toString() {
+ return "EMPTY";
+ }
+ };
+
+ /**
+ * @return The parent resolver that can be used to query for missing
+ * properties - {@code null} if no parent
+ */
+ PropertyResolver getParentPropertyResolver();
+
+ /**
+ * <P>
+ * A map of properties that can be used to configure the SSH server or
+ * client. This map will never be changed by either the server or client and
+ * is not supposed to be changed at runtime (changes are not bound to have
+ * any effect on a running client or server), though it may affect the
+ * creation of sessions later as these values are usually not cached.
+ * </P>
+ *
+ * <P>
+ * <B>Note:</B> the <U>type</U> of the mapped property should match the
+ * expected configuration value type - {@code Long, Integer, Boolean,
+ * String}, etc.... If it doesn't, the {@code toString()} result of the
+ * mapped value is used to convert it to the required type. E.g., if the
+ * mapped value is the <U>string</U> "1234" and the expected value
+ * is a {@code long} then it will be parsed into one. Also, if the mapped
+ * value is an {@code Integer} but a {@code long} is expected, then it will
+ * be converted into one.
+ * </P>
+ *
+ * @return a valid <code>Map</code> containing configuration values, never
+ * {@code null}
+ */
+ Map<String, Object> getProperties();
+
+ default long getLongProperty(String name, long def) {
+ return PropertyResolverUtils.getLongProperty(this, name, def);
+ }
+
+ default Long getLong(String name) {
+ return PropertyResolverUtils.getLong(this, name);
+ }
+
+ default int getIntProperty(String name, int def) {
+ return PropertyResolverUtils.getIntProperty(this, name, def);
+ }
+
+ default Integer getInteger(String name) {
+ return PropertyResolverUtils.getInteger(this, name);
+ }
+
+ default boolean getBooleanProperty(String name, boolean def) {
+ return PropertyResolverUtils.getBooleanProperty(this, name, def);
+ }
+
+ default Boolean getBoolean(String name) {
+ return PropertyResolverUtils.getBoolean(this, name);
+ }
+
+ default String getStringProperty(String name, String def) {
+ return PropertyResolverUtils.getStringProperty(this, name, def);
+ }
+
+ default String getString(String name) {
+ return PropertyResolverUtils.getString(this, name);
+ }
+
+ default Object getObject(String name) {
+ return PropertyResolverUtils.getObject(this, name);
+ }
+}
[22/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java
deleted file mode 100644
index b59dbd0..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyRandomArt.java
+++ /dev/null
@@ -1,310 +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.StreamCorruptedException;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Objects;
-
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.digest.Digest;
-import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Draw an ASCII-Art representing the fingerprint so human brain can
- * profit from its built-in pattern recognition ability.
- * This technique is called "random art" and can be found in some
- * scientific publications like this original paper:
- *
- * "Hash Visualization: a New Technique to improve Real-World Security",
- * Perrig A. and Song D., 1999, International Workshop on Cryptographic
- * Techniques and E-Commerce (CrypTEC '99)
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <a href="http://sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf">Original article</a>
- * @see <a href="http://opensource.apple.com/source/OpenSSH/OpenSSH-175/openssh/key.c">C implementation</a>
- */
-public class KeyRandomArt {
- public static final int FLDBASE = 8;
- public static final int FLDSIZE_Y = FLDBASE + 1;
- public static final int FLDSIZE_X = FLDBASE * 2 + 1;
- public static final String AUGMENTATION_STRING = " .o+=*BOX@%&#/^SE";
-
- private final String algorithm;
- private final int keySize;
- private final char[][] field = new char[FLDSIZE_X][FLDSIZE_Y];
-
- public KeyRandomArt(PublicKey key) throws Exception {
- this(key, KeyUtils.getDefaultFingerPrintFactory());
- }
-
- public KeyRandomArt(PublicKey key, Factory<? extends Digest> f) throws Exception {
- this(key, Objects.requireNonNull(f, "No digest factory").create());
- }
-
- public KeyRandomArt(PublicKey key, Digest d) throws Exception {
- this(Objects.requireNonNull(key, "No key provided").getAlgorithm(),
- KeyUtils.getKeySize(key),
- KeyUtils.getRawFingerprint(Objects.requireNonNull(d, "No key digest"), key));
- }
-
- /**
- * @param algorithm The key algorithm
- * @param keySize The key size in bits
- * @param digest The key digest
- */
- public KeyRandomArt(String algorithm, int keySize, byte[] digest) {
- this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm provided");
- ValidateUtils.checkTrue(keySize > 0, "Invalid key size: %d", keySize);
- this.keySize = keySize;
- Objects.requireNonNull(digest, "No key digest provided");
-
- int x = FLDSIZE_X / 2;
- int y = FLDSIZE_Y / 2;
- int len = AUGMENTATION_STRING.length() - 1;
- for (int i = 0; i < digest.length; i++) {
- /* each byte conveys four 2-bit move commands */
- int input = digest[i] & 0xFF;
- for (int b = 0; b < 4; b++) {
- /* evaluate 2 bit, rest is shifted later */
- x += ((input & 0x1) != 0) ? 1 : -1;
- y += ((input & 0x2) != 0) ? 1 : -1;
-
- /* assure we are still in bounds */
- x = Math.max(x, 0);
- y = Math.max(y, 0);
- x = Math.min(x, FLDSIZE_X - 1);
- y = Math.min(y, FLDSIZE_Y - 1);
-
- /* augment the field */
- if (field[x][y] < (len - 2)) {
- field[x][y]++;
- }
- input = input >> 2;
- }
- }
-
- /* mark starting point and end point*/
- field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = (char) (len - 1);
- field[x][y] = (char) len;
- }
-
- public String getAlgorithm() {
- return algorithm;
- }
-
- public int getKeySize() {
- return keySize;
- }
-
- /**
- * Outputs the generated random art
- *
- * @param <A> The {@link Appendable} output writer
- * @param sb The writer
- * @return The updated writer instance
- * @throws IOException If failed to write the combined result
- */
- public <A extends Appendable> A append(A sb) throws IOException {
- // Upper border
- String s = String.format("+--[%4s %4d]", getAlgorithm(), getKeySize());
- sb.append(s);
- for (int index = s.length(); index <= FLDSIZE_X; index++) {
- sb.append('-');
- }
- sb.append('+');
- sb.append('\n');
-
- // contents
- int len = AUGMENTATION_STRING.length() - 1;
- for (int y = 0; y < FLDSIZE_Y; y++) {
- sb.append('|');
- for (int x = 0; x < FLDSIZE_X; x++) {
- char ch = field[x][y];
- sb.append(AUGMENTATION_STRING.charAt(Math.min(ch, len)));
- }
- sb.append('|');
- sb.append('\n');
- }
-
- // lower border
- sb.append('+');
- for (int index = 0; index < FLDSIZE_X; index++) {
- sb.append('-');
- }
-
- sb.append('+');
- sb.append('\n');
- return sb;
- }
-
- @Override
- public String toString() {
- try {
- return append(new StringBuilder((FLDSIZE_X + 4) * (FLDSIZE_Y + 3))).toString();
- } catch (IOException e) {
- return e.getClass().getSimpleName(); // unexpected
- }
- }
-
- /**
- * Combines the arts in a user-friendly way so they are aligned with each other
- *
- * @param separator The separator to use between the arts - if empty char
- * ('\0') then no separation is done
- * @param arts The {@link KeyRandomArt}s to combine - ignored if {@code null}/empty
- * @return The combined result
- */
- public static String combine(char separator, Collection<? extends KeyRandomArt> arts) {
- if (GenericUtils.isEmpty(arts)) {
- return "";
- }
-
- try {
- return combine(new StringBuilder(arts.size() * (FLDSIZE_X + 4) * (FLDSIZE_Y + 3)), separator, arts).toString();
- } catch (IOException e) {
- return e.getClass().getSimpleName(); // unexpected
- }
- }
-
- /**
- * Creates the combined representation of the random art entries for the provided keys
- *
- * @param separator The separator to use between the arts - if empty char
- * ('\0') then no separation is done
- * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
- * or has no keys to provide
- * @return The combined representation
- * @throws Exception If failed to extract or combine the entries
- * @see #combine(Appendable, char, KeyIdentityProvider)
- */
- public static String combine(char separator, KeyIdentityProvider provider) throws Exception {
- return combine(new StringBuilder(4 * (FLDSIZE_X + 4) * (FLDSIZE_Y + 3)), separator, provider).toString();
- }
-
- /**
- * Appends the combined random art entries for the provided keys
- *
- * @param <A> The {@link Appendable} output writer
- * @param sb The writer
- * @param separator The separator to use between the arts - if empty char
- * ('\0') then no separation is done
- * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
- * or has no keys to provide
- * @return The updated writer instance
- * @throws Exception If failed to extract or write the entries
- * @see #generate(KeyIdentityProvider)
- * @see #combine(Appendable, char, Collection)
- */
- public static <A extends Appendable> A combine(A sb, char separator, KeyIdentityProvider provider) throws Exception {
- return combine(sb, separator, generate(provider));
- }
-
- /**
- * Extracts and generates random art entries for all key in the provider
- *
- * @param provider The {@link KeyIdentityProvider} - ignored if {@code null}
- * or has no keys to provide
- * @return The extracted {@link KeyRandomArt}s
- * @throws Exception If failed to extract the entries
- * @see KeyIdentityProvider#loadKeys()
- */
- public static Collection<KeyRandomArt> generate(KeyIdentityProvider provider) throws Exception {
- Iterable<KeyPair> keys = (provider == null) ? null : provider.loadKeys();
- Iterator<KeyPair> iter = (keys == null) ? null : keys.iterator();
- if ((iter == null) || (!iter.hasNext())) {
- return Collections.emptyList();
- }
-
- Collection<KeyRandomArt> arts = new LinkedList<>();
- do {
- KeyPair kp = iter.next();
- KeyRandomArt a = new KeyRandomArt(kp.getPublic());
- arts.add(a);
- } while (iter.hasNext());
-
- return arts;
- }
-
- /**
- * Combines the arts in a user-friendly way so they are aligned with each other
- *
- * @param <A> The {@link Appendable} output writer
- * @param sb The writer
- * @param separator The separator to use between the arts - if empty char
- * ('\0') then no separation is done
- * @param arts The {@link KeyRandomArt}s to combine - ignored if {@code null}/empty
- * @return The updated writer instance
- * @throws IOException If failed to write the combined result
- */
- public static <A extends Appendable> A combine(A sb, char separator, Collection<? extends KeyRandomArt> arts) throws IOException {
- if (GenericUtils.isEmpty(arts)) {
- return sb;
- }
-
- List<String[]> allLines = new ArrayList<>(arts.size());
- int numLines = -1;
- for (KeyRandomArt a : arts) {
- String s = a.toString();
- String[] lines = GenericUtils.split(s, '\n');
- if (numLines <= 0) {
- numLines = lines.length;
- } else {
- if (numLines != lines.length) {
- throw new StreamCorruptedException("Mismatched lines count: expected=" + numLines + ", actual=" + lines.length);
- }
- }
-
- for (int index = 0; index < lines.length; index++) {
- String l = lines[index];
- if ((l.length() > 0) && (l.charAt(l.length() - 1) == '\r')) {
- l = l.substring(0, l.length() - 1);
- lines[index] = l;
- }
- }
-
- allLines.add(lines);
- }
-
- for (int row = 0; row < numLines; row++) {
- for (int index = 0; index < allLines.size(); index++) {
- String[] lines = allLines.get(index);
- String l = lines[row];
- sb.append(l);
- if ((index > 0) && (separator != '\0')) {
- sb.append(separator);
- }
- }
- sb.append('\n');
- }
-
- return sb;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
deleted file mode 100644
index 84b019f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
+++ /dev/null
@@ -1,937 +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.math.BigInteger;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.nio.file.attribute.PosixFilePermission;
-import java.security.GeneralSecurityException;
-import java.security.Key;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.interfaces.DSAKey;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.interfaces.ECKey;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAKey;
-import java.security.interfaces.RSAPrivateCrtKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.DSAPublicKeySpec;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.impl.DSSPublicKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.impl.ECDSAPublicKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.impl.RSAPublicKeyDecoder;
-import org.apache.sshd.common.digest.BuiltinDigests;
-import org.apache.sshd.common.digest.Digest;
-import org.apache.sshd.common.digest.DigestFactory;
-import org.apache.sshd.common.digest.DigestUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.OsUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Utility class for keys
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class KeyUtils {
- /**
- * Name of algorithm for RSA keys to be used when calling security provider
- */
- public static final String RSA_ALGORITHM = "RSA";
-
- /**
- * The most commonly used RSA public key exponent
- */
- public static final BigInteger DEFAULT_RSA_PUBLIC_EXPONENT = new BigInteger("65537");
-
- /**
- * Name of algorithm for DSS keys to be used when calling security provider
- */
- public static final String DSS_ALGORITHM = "DSA";
-
- /**
- * Name of algorithm for EC keys to be used when calling security provider
- */
- public static final String EC_ALGORITHM = "EC";
-
- /**
- * The {@link Set} of {@link PosixFilePermission} <U>not</U> allowed if strict
- * permissions are enforced on key files
- */
- public static final Set<PosixFilePermission> STRICTLY_PROHIBITED_FILE_PERMISSION =
- Collections.unmodifiableSet(
- EnumSet.of(PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE, PosixFilePermission.GROUP_EXECUTE,
- PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_WRITE, PosixFilePermission.OTHERS_EXECUTE));
-
- /**
- * System property that can be used to control the default fingerprint factory used for keys.
- * If not set the {@link #DEFAULT_FINGERPRINT_DIGEST_FACTORY} is used
- */
- public static final String KEY_FINGERPRINT_FACTORY_PROP = "org.apache.sshd.keyFingerprintFactory";
-
- /**
- * The default {@link Factory} of {@link Digest}s initialized
- * as the value of {@link #getDefaultFingerPrintFactory()} if not
- * overridden by {@link #KEY_FINGERPRINT_FACTORY_PROP} or
- * {@link #setDefaultFingerPrintFactory(DigestFactory)}
- */
- public static final DigestFactory DEFAULT_FINGERPRINT_DIGEST_FACTORY = BuiltinDigests.sha256;
-
- private static final AtomicReference<DigestFactory> DEFAULT_DIGEST_HOLDER = new AtomicReference<>();
-
- private static final Map<String, PublicKeyEntryDecoder<?, ?>> BY_KEY_TYPE_DECODERS_MAP =
- new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
- private static final Map<Class<?>, PublicKeyEntryDecoder<?, ?>> BY_KEY_CLASS_DECODERS_MAP =
- new HashMap<>();
-
- static {
- registerPublicKeyEntryDecoder(RSAPublicKeyDecoder.INSTANCE);
- registerPublicKeyEntryDecoder(DSSPublicKeyEntryDecoder.INSTANCE);
-
- if (SecurityUtils.isECCSupported()) {
- registerPublicKeyEntryDecoder(ECDSAPublicKeyEntryDecoder.INSTANCE);
- }
- if (SecurityUtils.isEDDSACurveSupported()) {
- registerPublicKeyEntryDecoder(SecurityUtils.getEDDSAPublicKeyEntryDecoder());
- }
- }
-
- private KeyUtils() {
- throw new UnsupportedOperationException("No instance");
- }
-
- /**
- * <P>Checks if a path has strict permissions</P>
- * <UL>
- * <LI><P>
- * The path may not have {@link PosixFilePermission#OTHERS_EXECUTE}
- * permission
- * </P></LI>
- *
- * <LI><P>
- * (For {@code Unix}) The path may not have group or others permissions
- * </P></LI>
- *
- * <LI><P>
- * (For {@code Unix}) If the path is a file, then its folder may not have
- * group or others permissions
- * </P></LI>
- *
- * <LI><P>
- * The path must be owned by current user.
- * </P></LI>
- *
- * <LI><P>
- * (For {@code Unix}) The path may be owned by root.
- * </P></LI>
- *
- * <LI><P>
- * (For {@code Unix}) If the path is a file, then its folder must also
- * have valid owner.
- * </P></LI>
- *
- * </UL>
- *
- * @param path The {@link Path} to be checked - ignored if {@code null}
- * or does not exist
- * @param options The {@link LinkOption}s to use to query the file's permissions
- * @return The violated permission as {@link SimpleImmutableEntry} where key is a message and
- * value is the offending object {@link PosixFilePermission} or {@link String} for owner - {@code null}
- * if no violations detected
- * @throws IOException If failed to retrieve the permissions
- * @see #STRICTLY_PROHIBITED_FILE_PERMISSION
- */
- public static SimpleImmutableEntry<String, Object> validateStrictKeyFilePermissions(Path path, LinkOption... options) throws IOException {
- if ((path == null) || (!Files.exists(path, options))) {
- return null;
- }
-
- Collection<PosixFilePermission> perms = IoUtils.getPermissions(path, options);
- if (GenericUtils.isEmpty(perms)) {
- return null;
- }
-
- if (perms.contains(PosixFilePermission.OTHERS_EXECUTE)) {
- PosixFilePermission p = PosixFilePermission.OTHERS_EXECUTE;
- return new SimpleImmutableEntry<>(String.format("Permissions violation (%s)", p), p);
- }
-
- if (OsUtils.isUNIX()) {
- PosixFilePermission p = IoUtils.validateExcludedPermissions(perms, STRICTLY_PROHIBITED_FILE_PERMISSION);
- if (p != null) {
- return new SimpleImmutableEntry<>(String.format("Permissions violation (%s)", p), p);
- }
-
- if (Files.isRegularFile(path, options)) {
- Path parent = path.getParent();
- p = IoUtils.validateExcludedPermissions(IoUtils.getPermissions(parent, options), STRICTLY_PROHIBITED_FILE_PERMISSION);
- if (p != null) {
- return new SimpleImmutableEntry<>(String.format("Parent permissions violation (%s)", p), p);
- }
- }
- }
-
- String owner = IoUtils.getFileOwner(path, options);
- if (GenericUtils.isEmpty(owner)) {
- // we cannot get owner
- // general issue: jvm does not support permissions
- // security issue: specific filesystem does not support permissions
- return null;
- }
-
- String current = OsUtils.getCurrentUser();
- Set<String> expected = new HashSet<>();
- expected.add(current);
- if (OsUtils.isUNIX()) {
- // Windows "Administrator" was considered however in Windows most likely a group is used.
- expected.add(OsUtils.ROOT_USER);
- }
-
- if (!expected.contains(owner)) {
- return new SimpleImmutableEntry<>(String.format("Owner violation (%s)", owner), owner);
- }
-
- if (OsUtils.isUNIX()) {
- if (Files.isRegularFile(path, options)) {
- String parentOwner = IoUtils.getFileOwner(path.getParent(), options);
- if ((!GenericUtils.isEmpty(parentOwner)) && (!expected.contains(parentOwner))) {
- return new SimpleImmutableEntry<>(String.format("Parent owner violation (%s)", parentOwner), parentOwner);
- }
- }
- }
-
- return null;
- }
-
- /**
- * @param keyType The key type - {@code OpenSSH} name - e.g., {@code ssh-rsa, ssh-dss}
- * @param keySize The key size (in bits)
- * @return A {@link KeyPair} of the specified type and size
- * @throws GeneralSecurityException If failed to generate the key pair
- * @see #getPublicKeyEntryDecoder(String)
- * @see PublicKeyEntryDecoder#generateKeyPair(int)
- */
- public static KeyPair generateKeyPair(String keyType, int keySize) throws GeneralSecurityException {
- PublicKeyEntryDecoder<?, ?> decoder = getPublicKeyEntryDecoder(keyType);
- if (decoder == null) {
- throw new InvalidKeySpecException("No decoder for key type=" + keyType);
- }
-
- return decoder.generateKeyPair(keySize);
- }
-
- /**
- * Performs a deep-clone of the original {@link KeyPair} - i.e., creates
- * <U>new</U> public/private keys that are clones of the original one
- *
- * @param keyType The key type - {@code OpenSSH} name - e.g., {@code ssh-rsa, ssh-dss}
- * @param kp The {@link KeyPair} to clone - ignored if {@code null}
- * @return The cloned instance
- * @throws GeneralSecurityException If failed to clone the pair
- */
- public static KeyPair cloneKeyPair(String keyType, KeyPair kp) throws GeneralSecurityException {
- PublicKeyEntryDecoder<?, ?> decoder = getPublicKeyEntryDecoder(keyType);
- if (decoder == null) {
- throw new InvalidKeySpecException("No decoder for key type=" + keyType);
- }
-
- return decoder.cloneKeyPair(kp);
- }
-
- /**
- * @param decoder The decoder to register
- * @throws IllegalArgumentException if no decoder or not key type or no
- * supported names for the decoder
- * @see PublicKeyEntryDecoder#getPublicKeyType()
- * @see PublicKeyEntryDecoder#getSupportedTypeNames()
- */
- public static void registerPublicKeyEntryDecoder(PublicKeyEntryDecoder<?, ?> decoder) {
- Objects.requireNonNull(decoder, "No decoder specified");
-
- Class<?> pubType = Objects.requireNonNull(decoder.getPublicKeyType(), "No public key type declared");
- Class<?> prvType = Objects.requireNonNull(decoder.getPrivateKeyType(), "No private key type declared");
- synchronized (BY_KEY_CLASS_DECODERS_MAP) {
- BY_KEY_CLASS_DECODERS_MAP.put(pubType, decoder);
- BY_KEY_CLASS_DECODERS_MAP.put(prvType, decoder);
- }
-
- Collection<String> names = ValidateUtils.checkNotNullAndNotEmpty(decoder.getSupportedTypeNames(), "No supported key type");
- synchronized (BY_KEY_TYPE_DECODERS_MAP) {
- for (String n : names) {
- PublicKeyEntryDecoder<?, ?> prev = BY_KEY_TYPE_DECODERS_MAP.put(n, decoder);
- if (prev != null) {
- //noinspection UnnecessaryContinue
- continue; // debug breakpoint
- }
- }
- }
- }
-
- /**
- * @param keyType The {@code OpenSSH} key type string - e.g., {@code ssh-rsa, ssh-dss}
- * - ignored if {@code null}/empty
- * @return The registered {@link PublicKeyEntryDecoder} or {code null} if not found
- */
- public static PublicKeyEntryDecoder<?, ?> getPublicKeyEntryDecoder(String keyType) {
- if (GenericUtils.isEmpty(keyType)) {
- return null;
- }
-
- synchronized (BY_KEY_TYPE_DECODERS_MAP) {
- return BY_KEY_TYPE_DECODERS_MAP.get(keyType);
- }
- }
-
- /**
- * @param kp The {@link KeyPair} to examine - ignored if {@code null}
- * @return The matching {@link PublicKeyEntryDecoder} provided <U>both</U>
- * the public and private keys have the same decoder - {@code null} if no
- * match found
- * @see #getPublicKeyEntryDecoder(Key)
- */
- public static PublicKeyEntryDecoder<?, ?> getPublicKeyEntryDecoder(KeyPair kp) {
- if (kp == null) {
- return null;
- }
-
- PublicKeyEntryDecoder<?, ?> d1 = getPublicKeyEntryDecoder(kp.getPublic());
- PublicKeyEntryDecoder<?, ?> d2 = getPublicKeyEntryDecoder(kp.getPrivate());
- if (d1 == d2) {
- return d1;
- } else {
- return null; // some kind of mixed keys...
- }
- }
-
- /**
- * @param key The {@link Key} (public or private) - ignored if {@code null}
- * @return The registered {@link PublicKeyEntryDecoder} for this key or {code null} if no match found
- * @see #getPublicKeyEntryDecoder(Class)
- */
- public static PublicKeyEntryDecoder<?, ?> getPublicKeyEntryDecoder(Key key) {
- if (key == null) {
- return null;
- } else {
- return getPublicKeyEntryDecoder(key.getClass());
- }
- }
-
- /**
- * @param keyType The key {@link Class} - ignored if {@code null} or not a {@link Key}
- * compatible type
- * @return The registered {@link PublicKeyEntryDecoder} or {code null} if no match found
- */
- public static PublicKeyEntryDecoder<?, ?> getPublicKeyEntryDecoder(Class<?> keyType) {
- if ((keyType == null) || (!Key.class.isAssignableFrom(keyType))) {
- return null;
- }
-
- synchronized (BY_KEY_TYPE_DECODERS_MAP) {
- PublicKeyEntryDecoder<?, ?> decoder = BY_KEY_CLASS_DECODERS_MAP.get(keyType);
- if (decoder != null) {
- return decoder;
- }
-
- // in case it is a derived class
- for (PublicKeyEntryDecoder<?, ?> dec : BY_KEY_CLASS_DECODERS_MAP.values()) {
- Class<?> pubType = dec.getPublicKeyType();
- Class<?> prvType = dec.getPrivateKeyType();
- if (pubType.isAssignableFrom(keyType) || prvType.isAssignableFrom(keyType)) {
- return dec;
- }
- }
- }
-
- return null;
- }
-
- /**
- * @return The default {@link DigestFactory}
- * by the {@link #getFingerPrint(PublicKey)} and {@link #getFingerPrint(String)}
- * methods
- * @see #KEY_FINGERPRINT_FACTORY_PROP
- * @see #setDefaultFingerPrintFactory(DigestFactory)
- */
- public static DigestFactory getDefaultFingerPrintFactory() {
- DigestFactory factory = null;
- synchronized (DEFAULT_DIGEST_HOLDER) {
- factory = DEFAULT_DIGEST_HOLDER.get();
- if (factory != null) {
- return factory;
- }
-
- String propVal = System.getProperty(KEY_FINGERPRINT_FACTORY_PROP);
- if (GenericUtils.isEmpty(propVal)) {
- factory = DEFAULT_FINGERPRINT_DIGEST_FACTORY;
- } else {
- factory = ValidateUtils.checkNotNull(BuiltinDigests.fromFactoryName(propVal), "Unknown digest factory: %s", propVal);
- }
-
- ValidateUtils.checkTrue(factory.isSupported(), "Selected fingerprint digest not supported: %s", factory.getName());
- DEFAULT_DIGEST_HOLDER.set(factory);
- }
-
- return factory;
- }
-
- /**
- * @param f The {@link DigestFactory} of {@link Digest}s to be used - may
- * not be {@code null}
- */
- public static void setDefaultFingerPrintFactory(DigestFactory f) {
- synchronized (DEFAULT_DIGEST_HOLDER) {
- DEFAULT_DIGEST_HOLDER.set(Objects.requireNonNull(f, "No digest factory"));
- }
- }
-
- /**
- * @param key the public key - ignored if {@code null}
- * @return the fingerprint or {@code null} if no key.
- * <B>Note:</B> if exception encountered then returns the exception's simple class name
- * @see #getFingerPrint(Factory, PublicKey)
- */
- public static String getFingerPrint(PublicKey key) {
- return getFingerPrint(getDefaultFingerPrintFactory(), key);
- }
-
- /**
- * @param password The {@link String} to digest - ignored if {@code null}/empty,
- * otherwise its UTF-8 representation is used as input for the fingerprint
- * @return The fingerprint - {@code null} if {@code null}/empty input.
- * <B>Note:</B> if exception encountered then returns the exception's simple class name
- * @see #getFingerPrint(String, Charset)
- */
- public static String getFingerPrint(String password) {
- return getFingerPrint(password, StandardCharsets.UTF_8);
- }
-
- /**
- * @param password The {@link String} to digest - ignored if {@code null}/empty
- * @param charset The {@link Charset} to use in order to convert the
- * string to its byte representation to use as input for the fingerprint
- * @return The fingerprint - {@code null} if {@code null}/empty input.
- * <B>Note:</B> if exception encountered then returns the exception's simple class name
- * @see #getFingerPrint(Factory, String, Charset)
- * @see #getDefaultFingerPrintFactory()
- */
- public static String getFingerPrint(String password, Charset charset) {
- return getFingerPrint(getDefaultFingerPrintFactory(), password, charset);
- }
-
- /**
- * @param f The {@link Factory} to create the {@link Digest} to use
- * @param key the public key - ignored if {@code null}
- * @return the fingerprint or {@code null} if no key.
- * <B>Note:</B> if exception encountered then returns the exception's simple class name
- * @see #getFingerPrint(Digest, PublicKey)
- */
- public static String getFingerPrint(Factory<? extends Digest> f, PublicKey key) {
- return (key == null) ? null : getFingerPrint(Objects.requireNonNull(f, "No digest factory").create(), key);
- }
-
- /**
- * @param d The {@link Digest} to use
- * @param key the public key - ignored if {@code null}
- * @return the fingerprint or {@code null} if no key.
- * <B>Note:</B> if exception encountered then returns the exception's simple class name
- * @see DigestUtils#getFingerPrint(Digest, byte[], int, int)
- */
- public static String getFingerPrint(Digest d, PublicKey key) {
- if (key == null) {
- return null;
- }
-
- try {
- Buffer buffer = new ByteArrayBuffer();
- buffer.putRawPublicKey(key);
- return DigestUtils.getFingerPrint(d, buffer.array(), 0, buffer.wpos());
- } catch (Exception e) {
- return e.getClass().getSimpleName();
- }
- }
-
- public static byte[] getRawFingerprint(PublicKey key) throws Exception {
- return getRawFingerprint(getDefaultFingerPrintFactory(), key);
- }
-
- public static byte[] getRawFingerprint(Factory<? extends Digest> f, PublicKey key) throws Exception {
- return (key == null) ? null : getRawFingerprint(Objects.requireNonNull(f, "No digest factory").create(), key);
- }
-
- public static byte[] getRawFingerprint(Digest d, PublicKey key) throws Exception {
- if (key == null) {
- return null;
- }
-
- Buffer buffer = new ByteArrayBuffer();
- buffer.putRawPublicKey(key);
- return DigestUtils.getRawFingerprint(d, buffer.array(), 0, buffer.wpos());
- }
-
- /**
- * @param f The {@link Factory} to create the {@link Digest} to use
- * @param s The {@link String} to digest - ignored if {@code null}/empty,
- * otherwise its UTF-8 representation is used as input for the fingerprint
- * @return The fingerprint - {@code null} if {@code null}/empty input.
- * <B>Note:</B> if exception encountered then returns the exception's simple class name
- * @see #getFingerPrint(Digest, String, Charset)
- */
- public static String getFingerPrint(Factory<? extends Digest> f, String s) {
- return getFingerPrint(f, s, StandardCharsets.UTF_8);
- }
-
- /**
- * @param f The {@link Factory} to create the {@link Digest} to use
- * @param s The {@link String} to digest - ignored if {@code null}/empty
- * @param charset The {@link Charset} to use in order to convert the
- * string to its byte representation to use as input for the fingerprint
- * @return The fingerprint - {@code null} if {@code null}/empty input
- * <B>Note:</B> if exception encountered then returns the exception's simple class name
- * @see DigestUtils#getFingerPrint(Digest, String, Charset)
- */
- public static String getFingerPrint(Factory<? extends Digest> f, String s, Charset charset) {
- return getFingerPrint(f.create(), s, charset);
- }
-
- /**
- * @param d The {@link Digest} to use
- * @param s The {@link String} to digest - ignored if {@code null}/empty,
- * otherwise its UTF-8 representation is used as input for the fingerprint
- * @return The fingerprint - {@code null} if {@code null}/empty input.
- * <B>Note:</B> if exception encountered then returns the exception's simple class name
- * @see DigestUtils#getFingerPrint(Digest, String, Charset)
- */
- public static String getFingerPrint(Digest d, String s) {
- return getFingerPrint(d, s, StandardCharsets.UTF_8);
- }
-
- /**
- * @param d The {@link Digest} to use to calculate the fingerprint
- * @param s The string to digest - ignored if {@code null}/empty
- * @param charset The {@link Charset} to use in order to convert the
- * string to its byte representation to use as input for the fingerprint
- * @return The fingerprint - {@code null} if {@code null}/empty input.
- * <B>Note:</B> if exception encountered then returns the exception's simple class name
- * @see DigestUtils#getFingerPrint(Digest, String, Charset)
- */
- public static String getFingerPrint(Digest d, String s, Charset charset) {
- if (GenericUtils.isEmpty(s)) {
- return null;
- }
-
- try {
- return DigestUtils.getFingerPrint(d, s, charset);
- } catch (Exception e) {
- return e.getClass().getSimpleName();
- }
- }
-
- /**
- * @param expected The expected fingerprint if {@code null} or empty then returns a failure
- * with the default fingerprint.
- * @param key the {@link PublicKey} - if {@code null} then returns null.
- * @return SimpleImmutableEntry<Boolean, String> - key is success indicator, value is actual fingerprint,
- * {@code null} if no key.
- * @see #getDefaultFingerPrintFactory()
- * @see #checkFingerPrint(String, Factory, PublicKey)
- */
- public static SimpleImmutableEntry<Boolean, String> checkFingerPrint(String expected, PublicKey key) {
- return checkFingerPrint(expected, getDefaultFingerPrintFactory(), key);
- }
-
- /**
- * @param expected The expected fingerprint if {@code null} or empty then returns a failure
- * with the default fingerprint.
- * @param f The {@link Factory} to be used to generate the default {@link Digest} for the key
- * @param key the {@link PublicKey} - if {@code null} then returns null.
- * @return SimpleImmutableEntry<Boolean, String> - key is success indicator, value is actual fingerprint,
- * {@code null} if no key.
- */
- public static SimpleImmutableEntry<Boolean, String> checkFingerPrint(String expected, Factory<? extends Digest> f, PublicKey key) {
- return checkFingerPrint(expected, Objects.requireNonNull(f, "No digest factory").create(), key);
- }
-
- /**
- * @param expected The expected fingerprint if {@code null} or empty then returns a failure
- * with the default fingerprint.
- * @param d The {@link Digest} to be used to generate the default fingerprint for the key
- * @param key the {@link PublicKey} - if {@code null} then returns null.
- * @return SimpleImmutableEntry<Boolean, String> - key is success indicator, value is actual fingerprint,
- * {@code null} if no key.
- */
- public static SimpleImmutableEntry<Boolean, String> checkFingerPrint(String expected, Digest d, PublicKey key) {
- if (key == null) {
- return null;
- }
-
- if (GenericUtils.isEmpty(expected)) {
- return new SimpleImmutableEntry<>(false, getFingerPrint(d, key));
- }
-
- // de-construct fingerprint
- int pos = expected.indexOf(':');
- if ((pos < 0) || (pos >= (expected.length() - 1))) {
- return new SimpleImmutableEntry<>(false, getFingerPrint(d, key));
- }
-
- String name = expected.substring(0, pos);
- String value = expected.substring(pos + 1);
- DigestFactory expectedFactory;
- // We know that all digest names have a length > 2 - if 2 (or less) then assume a pure HEX value
- if (name.length() > 2) {
- expectedFactory = BuiltinDigests.fromFactoryName(name);
- if (expectedFactory == null) {
- return new SimpleImmutableEntry<>(false, getFingerPrint(d, key));
- }
-
- expected = name.toUpperCase() + ":" + value;
- } else {
- expectedFactory = BuiltinDigests.md5;
- expected = expectedFactory.getName().toUpperCase() + ":" + expected;
- }
-
- String fingerprint = getFingerPrint(expectedFactory, key);
- boolean matches = BuiltinDigests.md5.getName().equals(expectedFactory.getName())
- ? expected.equalsIgnoreCase(fingerprint) // HEX is case insensitive
- : expected.equals(fingerprint);
- return new SimpleImmutableEntry<>(matches, fingerprint);
- }
-
- /**
- * @param kp a key pair - ignored if {@code null}. If the private
- * key is non-{@code null} then it is used to determine the type,
- * otherwise the public one is used.
- * @return the key type or {@code null} if cannot determine it
- * @see #getKeyType(Key)
- */
- public static String getKeyType(KeyPair kp) {
- if (kp == null) {
- return null;
- }
- PrivateKey key = kp.getPrivate();
- if (key != null) {
- return getKeyType(key);
- } else {
- return getKeyType(kp.getPublic());
- }
- }
-
- /**
- * @param key a public or private key
- * @return the key type or {@code null} if cannot determine it
- */
- public static String getKeyType(Key key) {
- if (key == null) {
- return null;
- } else if (key instanceof DSAKey) {
- return KeyPairProvider.SSH_DSS;
- } else if (key instanceof RSAKey) {
- return KeyPairProvider.SSH_RSA;
- } else if (key instanceof ECKey) {
- ECKey ecKey = (ECKey) key;
- ECParameterSpec ecSpec = ecKey.getParams();
- ECCurves curve = ECCurves.fromCurveParameters(ecSpec);
- if (curve == null) {
- return null; // debug breakpoint
- } else {
- return curve.getKeyType();
- }
- } else if (SecurityUtils.EDDSA.equalsIgnoreCase(key.getAlgorithm())) {
- return KeyPairProvider.SSH_ED25519;
- }
-
- return null;
- }
-
- /**
- * Determines the key size in bits
- *
- * @param key The {@link Key} to examine - ignored if {@code null}
- * @return The key size - non-positive value if cannot determine it
- */
- public static int getKeySize(Key key) {
- if (key == null) {
- return -1;
- } else if (key instanceof RSAKey) {
- BigInteger n = ((RSAKey) key).getModulus();
- return n.bitLength();
- } else if (key instanceof DSAKey) {
- DSAParams params = ((DSAKey) key).getParams();
- BigInteger p = params.getP();
- return p.bitLength();
- } else if (key instanceof ECKey) {
- ECParameterSpec ecSpec = ((ECKey) key).getParams();
- ECCurves curve = ECCurves.fromCurveParameters(ecSpec);
- if (curve != null) {
- return curve.getKeySize();
- }
- } else if (SecurityUtils.EDDSA.equalsIgnoreCase(key.getAlgorithm())) {
- return SecurityUtils.getEDDSAKeySize(key);
- }
-
- return -1;
- }
-
- /**
- * @param key The {@link PublicKey} to be checked - ignored if {@code null}
- * @param keySet The keys to be searched - ignored if {@code null}/empty
- * @return The matching {@link PublicKey} from the keys or {@code null} if
- * no match found
- * @see #compareKeys(PublicKey, PublicKey)
- */
- public static PublicKey findMatchingKey(PublicKey key, PublicKey... keySet) {
- if (key == null || GenericUtils.isEmpty(keySet)) {
- return null;
- } else {
- return findMatchingKey(key, Arrays.asList(keySet));
- }
- }
-
- /**
- * @param key The {@link PublicKey} to be checked - ignored if {@code null}
- * @param keySet The keys to be searched - ignored if {@code null}/empty
- * @return The matching {@link PublicKey} from the keys or {@code null} if
- * no match found
- * @see #compareKeys(PublicKey, PublicKey)
- */
- public static PublicKey findMatchingKey(PublicKey key, Collection<? extends PublicKey> keySet) {
- if (key == null || GenericUtils.isEmpty(keySet)) {
- return null;
- }
- for (PublicKey k : keySet) {
- if (compareKeys(key, k)) {
- return k;
- }
- }
- return null;
- }
-
- public static boolean compareKeyPairs(KeyPair k1, KeyPair k2) {
- if (Objects.equals(k1, k2)) {
- return true;
- } else if ((k1 == null) || (k2 == null)) {
- return false; // both null is covered by Objects#equals
- } else {
- return compareKeys(k1.getPublic(), k2.getPublic())
- && compareKeys(k1.getPrivate(), k2.getPrivate());
- }
- }
-
- public static boolean compareKeys(PublicKey k1, PublicKey k2) {
- if ((k1 instanceof RSAPublicKey) && (k2 instanceof RSAPublicKey)) {
- return compareRSAKeys(RSAPublicKey.class.cast(k1), RSAPublicKey.class.cast(k2));
- } else if ((k1 instanceof DSAPublicKey) && (k2 instanceof DSAPublicKey)) {
- return compareDSAKeys(DSAPublicKey.class.cast(k1), DSAPublicKey.class.cast(k2));
- } else if ((k1 instanceof ECPublicKey) && (k2 instanceof ECPublicKey)) {
- return compareECKeys(ECPublicKey.class.cast(k1), ECPublicKey.class.cast(k2));
- } else if ((k1 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k1.getAlgorithm())
- && (k2 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k2.getAlgorithm())) {
- return SecurityUtils.compareEDDSAPPublicKeys(k1, k2);
- } else {
- return false; // either key is null or not of same class
- }
- }
-
- public static PublicKey recoverPublicKey(PrivateKey key) throws GeneralSecurityException {
- if (key instanceof RSAPrivateKey) {
- return recoverRSAPublicKey((RSAPrivateKey) key);
- } else if (key instanceof DSAPrivateKey) {
- return recoverDSAPublicKey((DSAPrivateKey) key);
- } else if ((key != null) && SecurityUtils.EDDSA.equalsIgnoreCase(key.getAlgorithm())) {
- return SecurityUtils.recoverEDDSAPublicKey(key);
- } else {
- return null;
- }
- }
-
- public static boolean compareKeys(PrivateKey k1, PrivateKey k2) {
- if ((k1 instanceof RSAPrivateKey) && (k2 instanceof RSAPrivateKey)) {
- return compareRSAKeys(RSAPrivateKey.class.cast(k1), RSAPrivateKey.class.cast(k2));
- } else if ((k1 instanceof DSAPrivateKey) && (k2 instanceof DSAPrivateKey)) {
- return compareDSAKeys(DSAPrivateKey.class.cast(k1), DSAPrivateKey.class.cast(k2));
- } else if ((k1 instanceof ECPrivateKey) && (k2 instanceof ECPrivateKey)) {
- return compareECKeys(ECPrivateKey.class.cast(k1), ECPrivateKey.class.cast(k2));
- } else if ((k1 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k1.getAlgorithm())
- && (k2 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k2.getAlgorithm())) {
- return SecurityUtils.compareEDDSAPrivateKeys(k1, k2);
- } else {
- return false; // either key is null or not of same class
- }
- }
-
- public static boolean compareRSAKeys(RSAPublicKey k1, RSAPublicKey k2) {
- if (Objects.equals(k1, k2)) {
- return true;
- } else if (k1 == null || k2 == null) {
- return false; // both null is covered by Objects#equals
- } else {
- return Objects.equals(k1.getPublicExponent(), k2.getPublicExponent())
- && Objects.equals(k1.getModulus(), k2.getModulus());
- }
- }
-
- public static boolean compareRSAKeys(RSAPrivateKey k1, RSAPrivateKey k2) {
- if (Objects.equals(k1, k2)) {
- return true;
- } else if (k1 == null || k2 == null) {
- return false; // both null is covered by Objects#equals
- } else {
- return Objects.equals(k1.getModulus(), k2.getModulus())
- && Objects.equals(k1.getPrivateExponent(), k2.getPrivateExponent());
- }
- }
-
- public static RSAPublicKey recoverRSAPublicKey(RSAPrivateKey privateKey) throws GeneralSecurityException {
- if (privateKey instanceof RSAPrivateCrtKey) {
- return recoverFromRSAPrivateCrtKey((RSAPrivateCrtKey) privateKey);
- } else {
- // Not ideal, but best we can do under the circumstances
- return recoverRSAPublicKey(privateKey.getModulus(), DEFAULT_RSA_PUBLIC_EXPONENT);
- }
- }
-
- public static RSAPublicKey recoverFromRSAPrivateCrtKey(RSAPrivateCrtKey rsaKey) throws GeneralSecurityException {
- return recoverRSAPublicKey(rsaKey.getPrimeP(), rsaKey.getPrimeQ(), rsaKey.getPublicExponent());
- }
-
- public static RSAPublicKey recoverRSAPublicKey(BigInteger p, BigInteger q, BigInteger publicExponent) throws GeneralSecurityException {
- return recoverRSAPublicKey(p.multiply(q), publicExponent);
- }
-
- public static RSAPublicKey recoverRSAPublicKey(BigInteger modulus, BigInteger publicExponent) throws GeneralSecurityException {
- KeyFactory kf = SecurityUtils.getKeyFactory(RSA_ALGORITHM);
- return (RSAPublicKey) kf.generatePublic(new RSAPublicKeySpec(modulus, publicExponent));
- }
-
- public static boolean compareDSAKeys(DSAPublicKey k1, DSAPublicKey k2) {
- if (Objects.equals(k1, k2)) {
- return true;
- } else if (k1 == null || k2 == null) {
- return false; // both null is covered by Objects#equals
- } else {
- return Objects.equals(k1.getY(), k2.getY())
- && compareDSAParams(k1.getParams(), k2.getParams());
- }
- }
-
- public static boolean compareDSAKeys(DSAPrivateKey k1, DSAPrivateKey k2) {
- if (Objects.equals(k1, k2)) {
- return true;
- } else if (k1 == null || k2 == null) {
- return false; // both null is covered by Objects#equals
- } else {
- return Objects.equals(k1.getX(), k2.getX())
- && compareDSAParams(k1.getParams(), k2.getParams());
- }
- }
-
- public static boolean compareDSAParams(DSAParams p1, DSAParams p2) {
- if (Objects.equals(p1, p2)) {
- return true;
- } else if (p1 == null || p2 == null) {
- return false; // both null is covered by Objects#equals
- } else {
- return Objects.equals(p1.getG(), p2.getG())
- && Objects.equals(p1.getP(), p2.getP())
- && Objects.equals(p1.getQ(), p2.getQ());
- }
- }
-
- // based on code from https://github.com/alexo/SAML-2.0/blob/master/java-opensaml/opensaml-security-api/src/main/java/org/opensaml/xml/security/SecurityHelper.java
- public static DSAPublicKey recoverDSAPublicKey(DSAPrivateKey privateKey) throws GeneralSecurityException {
- DSAParams keyParams = privateKey.getParams();
- BigInteger p = keyParams.getP();
- BigInteger x = privateKey.getX();
- BigInteger q = keyParams.getQ();
- BigInteger g = keyParams.getG();
- BigInteger y = g.modPow(x, p);
- KeyFactory kf = SecurityUtils.getKeyFactory(DSS_ALGORITHM);
- return (DSAPublicKey) kf.generatePublic(new DSAPublicKeySpec(y, p, q, g));
- }
-
- public static boolean compareECKeys(ECPrivateKey k1, ECPrivateKey k2) {
- if (Objects.equals(k1, k2)) {
- return true;
- } else if (k1 == null || k2 == null) {
- return false; // both null is covered by Objects#equals
- } else {
- return Objects.equals(k1.getS(), k2.getS())
- && compareECParams(k1.getParams(), k2.getParams());
- }
- }
-
- public static boolean compareECKeys(ECPublicKey k1, ECPublicKey k2) {
- if (Objects.equals(k1, k2)) {
- return true;
- } else if (k1 == null || k2 == null) {
- return false; // both null is covered by Objects#equals
- } else {
- return Objects.equals(k1.getW(), k2.getW())
- && compareECParams(k1.getParams(), k2.getParams());
- }
- }
-
- public static boolean compareECParams(ECParameterSpec s1, ECParameterSpec s2) {
- if (Objects.equals(s1, s2)) {
- return true;
- } else if (s1 == null || s2 == null) {
- return false; // both null is covered by Objects#equals
- } else {
- return Objects.equals(s1.getOrder(), s2.getOrder())
- && (s1.getCofactor() == s2.getCofactor())
- && Objects.equals(s1.getGenerator(), s2.getGenerator())
- && Objects.equals(s1.getCurve(), s2.getCurve());
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
deleted file mode 100644
index 6dbeee9..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryDecoder.java
+++ /dev/null
@@ -1,142 +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.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.StreamCorruptedException;
-import java.security.GeneralSecurityException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Collection;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * @param <PUB> Type of {@link PublicKey}
- * @param <PRV> Type of {@link PrivateKey}
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface PrivateKeyEntryDecoder<PUB extends PublicKey, PRV extends PrivateKey>
- extends KeyEntryResolver<PUB, PRV>, PrivateKeyEntryResolver {
-
- @Override
- default PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
- ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type provided");
- Collection<String> supported = getSupportedTypeNames();
- if ((GenericUtils.size(supported) > 0) && supported.contains(keyType)) {
- return decodePrivateKey(FilePasswordProvider.EMPTY, keyData);
- }
-
- throw new InvalidKeySpecException("resolve(" + keyType + ") not in listed supported types: " + supported);
- }
-
- /**
- * @param passwordProvider The {@link FilePasswordProvider} to use
- * in case the data is encrypted - may be {@code null} if no encrypted
- * data is expected
- * @param keyData The key data bytes in {@code OpenSSH} format (after
- * BASE64 decoding) - ignored if {@code null}/empty
- * @return The decoded {@link PrivateKey} - or {@code null} if no data
- * @throws IOException If failed to decode the key
- * @throws GeneralSecurityException If failed to generate the key
- */
- default PRV decodePrivateKey(FilePasswordProvider passwordProvider, byte... keyData)
- throws IOException, GeneralSecurityException {
- return decodePrivateKey(passwordProvider, keyData, 0, NumberUtils.length(keyData));
- }
-
- default PRV decodePrivateKey(FilePasswordProvider passwordProvider, byte[] keyData, int offset, int length)
- throws IOException, GeneralSecurityException {
- if (length <= 0) {
- return null;
- }
-
- try (InputStream stream = new ByteArrayInputStream(keyData, offset, length)) {
- return decodePrivateKey(passwordProvider, stream);
- }
- }
-
- default PRV decodePrivateKey(FilePasswordProvider passwordProvider, InputStream keyData)
- throws IOException, GeneralSecurityException {
- // the actual data is preceded by a string that repeats the key type
- String type = KeyEntryResolver.decodeString(keyData);
- if (GenericUtils.isEmpty(type)) {
- throw new StreamCorruptedException("Missing key type string");
- }
-
- Collection<String> supported = getSupportedTypeNames();
- if (GenericUtils.isEmpty(supported) || (!supported.contains(type))) {
- throw new InvalidKeySpecException("Reported key type (" + type + ") not in supported list: " + supported);
- }
-
- return decodePrivateKey(type, passwordProvider, keyData);
- }
-
- /**
- * @param keyType The reported / encode key type
- * @param passwordProvider The {@link FilePasswordProvider} to use
- * in case the data is encrypted - may be {@code null} if no encrypted
- * data is expected
- * @param keyData The key data bytes stream positioned after the key type decoding
- * and making sure it is one of the supported types
- * @return The decoded {@link PrivateKey}
- * @throws IOException If failed to read from the data stream
- * @throws GeneralSecurityException If failed to generate the key
- */
- PRV decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
- throws IOException, GeneralSecurityException;
-
- /**
- * Encodes the {@link PrivateKey} using the {@code OpenSSH} format - same
- * one used by the {@code decodePublicKey} method(s)
- *
- * @param s The {@link OutputStream} to write the data to
- * @param key The {@link PrivateKey} - may not be {@code null}
- * @return The key type value - one of the {@link #getSupportedTypeNames()} or
- * {@code null} if encoding not supported
- * @throws IOException If failed to generate the encoding
- */
- default String encodePrivateKey(OutputStream s, PRV key) throws IOException {
- Objects.requireNonNull(key, "No private key provided");
- return null;
- }
-
- default boolean isPublicKeyRecoverySupported() {
- return false;
- }
-
- /**
- * Attempts to recover the public key given the private one
- *
- * @param prvKey The {@link PrivateKey}
- * @return The recovered {@link PublicKey} - {@code null} if cannot recover it
- * @throws GeneralSecurityException If failed to generate the public key
- */
- default PUB recoverPublicKey(PRV prvKey) throws GeneralSecurityException {
- return null;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java
deleted file mode 100644
index 1e4c91e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PrivateKeyEntryResolver.java
+++ /dev/null
@@ -1,70 +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.security.GeneralSecurityException;
-import java.security.PrivateKey;
-import java.security.spec.InvalidKeySpecException;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface PrivateKeyEntryResolver {
- /**
- * A resolver that ignores all input
- */
- PrivateKeyEntryResolver IGNORING = new PrivateKeyEntryResolver() {
- @Override
- public PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
- return null;
- }
-
- @Override
- public String toString() {
- return "IGNORING";
- }
- };
-
- /**
- * A resolver that fails on all input
- */
- PrivateKeyEntryResolver FAILING = new PrivateKeyEntryResolver() {
- @Override
- public PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
- throw new InvalidKeySpecException("Failing resolver on key type=" + keyType);
- }
-
- @Override
- public String toString() {
- return "FAILING";
- }
- };
-
- /**
- * @param keyType The {@code OpenSSH} reported key type
- * @param keyData The {@code OpenSSH} encoded key data
- * @return The extracted {@link PrivateKey} - ignored if {@code null}
- * @throws IOException If failed to parse the key data
- * @throws GeneralSecurityException If failed to generate the key
- */
- PrivateKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java
deleted file mode 100644
index 1db3d2b..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntry.java
+++ /dev/null
@@ -1,286 +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.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.Serializable;
-import java.io.StreamCorruptedException;
-import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import java.util.Base64;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-
-/**
- * <P>Represents a {@link PublicKey} whose data is formatted according to
- * the <A HREF="http://en.wikibooks.org/wiki/OpenSSH">OpenSSH</A> format:</P>
- *
- * <PRE>
- * <key-type> <base64-encoded-public-key-data>
- * </PRE>
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class PublicKeyEntry implements Serializable {
-
- /**
- * Character used to denote a comment line in the keys file
- */
- public static final char COMMENT_CHAR = '#';
-
-
- /**
- * Standard folder name used by OpenSSH to hold key files
- */
- public static final String STD_KEYFILE_FOLDER_NAME = ".ssh";
-
- private static final long serialVersionUID = -585506072687602760L;
-
- private String keyType;
- private byte[] keyData;
-
- public PublicKeyEntry() {
- super();
- }
-
- public PublicKeyEntry(String keyType, byte... keyData) {
- this.keyType = keyType;
- this.keyData = keyData;
- }
-
- public String getKeyType() {
- return keyType;
- }
-
- public void setKeyType(String value) {
- this.keyType = value;
- }
-
- public byte[] getKeyData() {
- return keyData;
- }
-
- public void setKeyData(byte[] value) {
- this.keyData = value;
- }
-
- /**
- * @param fallbackResolver The {@link PublicKeyEntryResolver} to consult if
- * none of the built-in ones can be used. If {@code null} and no built-in
- * resolver can be used then an {@link InvalidKeySpecException} is thrown.
- * @return The resolved {@link PublicKey} - or {@code null} if could not be
- * resolved. <B>Note:</B> may be called only after key type and data bytes
- * have been set or exception(s) may be thrown
- * @throws IOException If failed to decode the key
- * @throws GeneralSecurityException If failed to generate the key
- */
- public PublicKey resolvePublicKey(PublicKeyEntryResolver fallbackResolver) throws IOException, GeneralSecurityException {
- String kt = getKeyType();
- PublicKeyEntryResolver decoder = KeyUtils.getPublicKeyEntryDecoder(kt);
- if (decoder == null) {
- decoder = fallbackResolver;
- }
- if (decoder == null) {
- throw new InvalidKeySpecException("No decoder available for key type=" + kt);
- }
-
- return decoder.resolve(kt, getKeyData());
- }
-
- /**
- * @param sb The {@link Appendable} instance to encode the data into
- * @param fallbackResolver The {@link PublicKeyEntryResolver} to consult if
- * none of the built-in ones can be used. If {@code null} and no built-in
- * resolver can be used then an {@link InvalidKeySpecException} is thrown.
- * @return The {@link PublicKey} or {@code null} if could not resolve it
- * @throws IOException If failed to decode/encode the key
- * @throws GeneralSecurityException If failed to generate the key
- * @see #resolvePublicKey(PublicKeyEntryResolver)
- */
- public PublicKey appendPublicKey(Appendable sb, PublicKeyEntryResolver fallbackResolver) throws IOException, GeneralSecurityException {
- PublicKey key = resolvePublicKey(fallbackResolver);
- if (key != null) {
- appendPublicKeyEntry(sb, key);
- }
- return key;
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(getKeyType()) + Arrays.hashCode(getKeyData());
- }
-
- /*
- * In case some derived class wants to define some "extended" equality
- * without having to repeat this code
- */
- protected boolean isEquivalent(PublicKeyEntry e) {
- if (this == e) {
- return true;
- }
- return Objects.equals(getKeyType(), e.getKeyType())
- && Arrays.equals(getKeyData(), e.getKeyData());
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (this == obj) {
- return true;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- return isEquivalent((PublicKeyEntry) obj);
- }
-
- @Override
- public String toString() {
- byte[] data = getKeyData();
- Base64.Encoder encoder = Base64.getEncoder();
- return getKeyType() + " " + (NumberUtils.isEmpty(data) ? "<no-key>" : encoder.encodeToString(data));
- }
-
- /**
- * @param encData Assumed to contain at least {@code key-type base64-data}
- * (anything beyond the BASE64 data is ignored) - ignored if {@code null}/empty
- * @return A {@link PublicKeyEntry} or {@code null} if no data
- * @throws IllegalArgumentException if bad format found
- * @see #parsePublicKeyEntry(PublicKeyEntry, String)
- */
- public static PublicKeyEntry parsePublicKeyEntry(String encData) throws IllegalArgumentException {
- String data = GenericUtils.replaceWhitespaceAndTrim(encData);
- if (GenericUtils.isEmpty(data)) {
- return null;
- } else {
- return parsePublicKeyEntry(new PublicKeyEntry(), data);
- }
- }
-
- /**
- * @param <E> The generic entry type
- * @param entry The {@link PublicKeyEntry} whose contents are to be
- * updated - ignored if {@code null}
- * @param encData Assumed to contain at least {@code key-type base64-data} (anything
- * beyond the BASE64 data is ignored) - ignored if {@code null}/empty
- * @return The updated entry instance
- * @throws IllegalArgumentException if bad format found
- */
- public static <E extends PublicKeyEntry> E parsePublicKeyEntry(E entry, String encData) throws IllegalArgumentException {
- String data = GenericUtils.replaceWhitespaceAndTrim(encData);
- if (GenericUtils.isEmpty(data) || (entry == null)) {
- return entry;
- }
-
- int startPos = data.indexOf(' ');
- if (startPos <= 0) {
- throw new IllegalArgumentException("Bad format (no key data delimiter): " + data);
- }
-
- int endPos = data.indexOf(' ', startPos + 1);
- if (endPos <= startPos) { // OK if no continuation beyond the BASE64 encoded data
- endPos = data.length();
- }
-
- String keyType = data.substring(0, startPos);
- String b64Data = data.substring(startPos + 1, endPos).trim();
- Base64.Decoder decoder = Base64.getDecoder();
- byte[] keyData = decoder.decode(b64Data);
- if (NumberUtils.isEmpty(keyData)) {
- throw new IllegalArgumentException("Bad format (no BASE64 key data): " + data);
- }
-
- entry.setKeyType(keyType);
- entry.setKeyData(keyData);
- return entry;
- }
-
- /**
- * @param key The {@link PublicKey}
- * @return The {@code OpenSSH} encoded data
- * @throws IllegalArgumentException If failed to encode
- * @see #appendPublicKeyEntry(Appendable, PublicKey)
- */
- public static String toString(PublicKey key) throws IllegalArgumentException {
- try {
- return appendPublicKeyEntry(new StringBuilder(Byte.MAX_VALUE), key).toString();
- } catch (IOException e) {
- throw new IllegalArgumentException("Failed (" + e.getClass().getSimpleName() + ") to encode: " + e.getMessage(), e);
- }
- }
-
- /**
- * Encodes a public key data the same way as the {@link #parsePublicKeyEntry(String)} expects it
- *
- * @param <A> The generic appendable class
- * @param sb The {@link Appendable} instance to encode the data into
- * @param key The {@link PublicKey} - ignored if {@code null}
- * @return The updated appendable instance
- * @throws IOException If failed to append the data
- */
- public static <A extends Appendable> A appendPublicKeyEntry(A sb, PublicKey key) throws IOException {
- if (key == null) {
- return sb;
- }
-
- @SuppressWarnings("unchecked")
- PublicKeyEntryDecoder<PublicKey, ?> decoder =
- (PublicKeyEntryDecoder<PublicKey, ?>) KeyUtils.getPublicKeyEntryDecoder(key);
- if (decoder == null) {
- throw new StreamCorruptedException("Cannot retrieve decoder for key=" + key.getAlgorithm());
- }
-
- try (ByteArrayOutputStream s = new ByteArrayOutputStream(Byte.MAX_VALUE)) {
- String keyType = decoder.encodePublicKey(s, key);
- byte[] bytes = s.toByteArray();
- Base64.Encoder encoder = Base64.getEncoder();
- String b64Data = encoder.encodeToString(bytes);
- sb.append(keyType).append(' ').append(b64Data);
- }
-
- return sb;
- }
-
- private static final class LazyDefaultKeysFolderHolder {
- private static final Path PATH =
- IdentityUtils.getUserHomeFolder().resolve(STD_KEYFILE_FOLDER_NAME);
-
- private LazyDefaultKeysFolderHolder() {
- throw new UnsupportedOperationException("No instance allowed");
- }
- }
-
- /**
- * @return The default OpenSSH folder used to hold key files - e.g.,
- * {@code known_hosts}, {@code authorized_keys}, etc.
- */
- @SuppressWarnings("synthetic-access")
- public static Path getDefaultKeysFolderPath() {
- return LazyDefaultKeysFolderHolder.PATH;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
deleted file mode 100644
index 7462e4a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDecoder.java
+++ /dev/null
@@ -1,114 +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.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.StreamCorruptedException;
-import java.security.GeneralSecurityException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Collection;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Represents a decoder of an {@code OpenSSH} encoded key data
- *
- * @param <PUB> Type of {@link PublicKey}
- * @param <PRV> Type of {@link PrivateKey}
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface PublicKeyEntryDecoder<PUB extends PublicKey, PRV extends PrivateKey>
- extends KeyEntryResolver<PUB, PRV>, PublicKeyEntryResolver {
-
- @Override
- default PublicKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
- ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type provided");
- Collection<String> supported = getSupportedTypeNames();
- if ((GenericUtils.size(supported) > 0) && supported.contains(keyType)) {
- return decodePublicKey(keyData);
- }
-
- throw new InvalidKeySpecException("resolve(" + keyType + ") not in listed supported types: " + supported);
- }
-
- /**
- * @param keyData The key data bytes in {@code OpenSSH} format (after
- * BASE64 decoding) - ignored if {@code null}/empty
- * @return The decoded {@link PublicKey} - or {@code null} if no data
- * @throws IOException If failed to decode the key
- * @throws GeneralSecurityException If failed to generate the key
- */
- default PUB decodePublicKey(byte... keyData) throws IOException, GeneralSecurityException {
- return decodePublicKey(keyData, 0, NumberUtils.length(keyData));
- }
-
- default PUB decodePublicKey(byte[] keyData, int offset, int length) throws IOException, GeneralSecurityException {
- if (length <= 0) {
- return null;
- }
-
- try (InputStream stream = new ByteArrayInputStream(keyData, offset, length)) {
- return decodePublicKey(stream);
- }
- }
-
- default PUB decodePublicKey(InputStream keyData) throws IOException, GeneralSecurityException {
- // the actual data is preceded by a string that repeats the key type
- String type = KeyEntryResolver.decodeString(keyData);
- if (GenericUtils.isEmpty(type)) {
- throw new StreamCorruptedException("Missing key type string");
- }
-
- Collection<String> supported = getSupportedTypeNames();
- if (GenericUtils.isEmpty(supported) || (!supported.contains(type))) {
- throw new InvalidKeySpecException("Reported key type (" + type + ") not in supported list: " + supported);
- }
-
- return decodePublicKey(type, keyData);
- }
-
- /**
- * @param keyType The reported / encode key type
- * @param keyData The key data bytes stream positioned after the key type decoding
- * and making sure it is one of the supported types
- * @return The decoded {@link PublicKey}
- * @throws IOException If failed to read from the data stream
- * @throws GeneralSecurityException If failed to generate the key
- */
- PUB decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException;
-
- /**
- * Encodes the {@link PublicKey} using the {@code OpenSSH} format - same
- * one used by the {@code decodePublicKey} method(s)
- *
- * @param s The {@link OutputStream} to write the data to
- * @param key The {@link PublicKey} - may not be {@code null}
- * @return The key type value - one of the {@link #getSupportedTypeNames()}
- * @throws IOException If failed to generate the encoding
- */
- String encodePublicKey(OutputStream s, PUB key) throws IOException;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryResolver.java
deleted file mode 100644
index e5eb0ed..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryResolver.java
+++ /dev/null
@@ -1,70 +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.security.GeneralSecurityException;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface PublicKeyEntryResolver {
- /**
- * A resolver that ignores all input
- */
- PublicKeyEntryResolver IGNORING = new PublicKeyEntryResolver() {
- @Override
- public PublicKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
- return null;
- }
-
- @Override
- public String toString() {
- return "IGNORING";
- }
- };
-
- /**
- * A resolver that fails on all input
- */
- PublicKeyEntryResolver FAILING = new PublicKeyEntryResolver() {
- @Override
- public PublicKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException {
- throw new InvalidKeySpecException("Failing resolver on key type=" + keyType);
- }
-
- @Override
- public String toString() {
- return "FAILING";
- }
- };
-
- /**
- * @param keyType The {@code OpenSSH} reported key type
- * @param keyData The {@code OpenSSH} encoded key data
- * @return The extracted {@link PublicKey} - ignored if {@code null}
- * @throws IOException If failed to parse the key data
- * @throws GeneralSecurityException If failed to generate the key
- */
- PublicKey resolve(String keyType, byte[] keyData) throws IOException, GeneralSecurityException;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractIdentityResourceLoader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractIdentityResourceLoader.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractIdentityResourceLoader.java
deleted file mode 100644
index a2412bd..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractIdentityResourceLoader.java
+++ /dev/null
@@ -1,62 +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.impl;
-
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.Collection;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.IdentityResourceLoader;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.logging.AbstractLoggingBean;
-
-/**
- * @param <PUB> Generic public key type
- * @param <PRV> Generic private key type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractIdentityResourceLoader<PUB extends PublicKey, PRV extends PrivateKey>
- extends AbstractLoggingBean
- implements IdentityResourceLoader<PUB, PRV> {
- private final Class<PUB> pubType;
- private final Class<PRV> prvType;
- private final Collection<String> names;
-
- protected AbstractIdentityResourceLoader(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
- this.pubType = Objects.requireNonNull(pubType, "No public key type specified");
- this.prvType = Objects.requireNonNull(prvType, "No private key type specified");
- this.names = ValidateUtils.checkNotNullAndNotEmpty(names, "No type names provided");
- }
-
- @Override
- public final Class<PUB> getPublicKeyType() {
- return pubType;
- }
-
- @Override
- public final Class<PRV> getPrivateKeyType() {
- return prvType;
- }
-
- @Override
- public Collection<String> getSupportedTypeNames() {
- return names;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.java
deleted file mode 100644
index 7afa3a6..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/impl/AbstractKeyEntryResolver.java
+++ /dev/null
@@ -1,59 +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.impl;
-
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.KeySpec;
-import java.util.Collection;
-
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-
-/**
- * @param <PUB> Type of {@link PublicKey}
- * @param <PRV> Type of {@link PrivateKey}
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractKeyEntryResolver<PUB extends PublicKey, PRV extends PrivateKey>
- extends AbstractIdentityResourceLoader<PUB, PRV>
- implements KeyEntryResolver<PUB, PRV> {
- protected AbstractKeyEntryResolver(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) {
- super(pubType, prvType, names);
- }
-
- public PUB generatePublicKey(KeySpec keySpec) throws GeneralSecurityException {
- KeyFactory factory = getKeyFactoryInstance();
- Class<PUB> keyType = getPublicKeyType();
- return keyType.cast(factory.generatePublic(keySpec));
- }
-
- public PRV generatePrivateKey(KeySpec keySpec) throws GeneralSecurityException {
- KeyFactory factory = getKeyFactoryInstance();
- Class<PRV> keyType = getPrivateKeyType();
- return keyType.cast(factory.generatePrivate(keySpec));
- }
-
- @Override
- public String toString() {
- return getPublicKeyType().getSimpleName() + ": " + getSupportedTypeNames();
- }
-}
[39/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/SelectorUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/SelectorUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/SelectorUtils.java
new file mode 100644
index 0000000..53bca90
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/SelectorUtils.java
@@ -0,0 +1,805 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.io.File;
+import java.nio.file.FileSystem;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.StringTokenizer;
+
+/**
+ * <p>This is a utility class used by selectors and DirectoryScanner. The
+ * functionality more properly belongs just to selectors, but unfortunately
+ * DirectoryScanner exposed these as protected methods. Thus we have to
+ * support any subclasses of DirectoryScanner that may access these methods.
+ * </p>
+ * <p>This is a Singleton.</p>
+ *
+ * @author Arnout J. Kuiper
+ * <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a>
+ * @author Magesh Umasankar
+ * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a>
+ * @version $Id$
+ * @since 1.5
+ */
+public final class SelectorUtils {
+
+ public static final String PATTERN_HANDLER_PREFIX = "[";
+
+ public static final String PATTERN_HANDLER_SUFFIX = "]";
+
+ public static final String REGEX_HANDLER_PREFIX = "%regex" + PATTERN_HANDLER_PREFIX;
+
+ public static final String ANT_HANDLER_PREFIX = "%ant" + PATTERN_HANDLER_PREFIX;
+
+ /**
+ * Private Constructor
+ */
+ private SelectorUtils() {
+ throw new UnsupportedOperationException("No instance allowed");
+ }
+
+ /**
+ * <p>Tests whether or not a given path matches the start of a given
+ * pattern up to the first "**".</p>
+ *
+ * <p>This is not a general purpose test and should only be used if you
+ * can live with false positives. For example, <code>pattern=**\a</code>
+ * and <code>str=b</code> will yield <code>true</code>.</p>
+ *
+ * @param pattern The pattern to match against. Must not be
+ * {@code null}.
+ * @param str The path to match, as a String. Must not be
+ * {@code null}.
+ * @return whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ */
+ public static boolean matchPatternStart(String pattern, String str) {
+ return matchPatternStart(pattern, str, true);
+ }
+
+ /**
+ * <p>Tests whether or not a given path matches the start of a given
+ * pattern up to the first "**".</p>
+ *
+ * <p>This is not a general purpose test and should only be used if you
+ * can live with false positives. For example, <code>pattern=**\a</code>
+ * and <code>str=b</code> will yield <code>true</code>.</p>
+ *
+ * @param pattern The pattern to match against. Must not be
+ * {@code null}.
+ * @param str The path to match, as a String. Must not be
+ * {@code null}.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ * @return whether or not a given path matches the start of a given
+ * pattern up to the first "**".
+ */
+ public static boolean matchPatternStart(String pattern, String str,
+ boolean isCaseSensitive) {
+ if (pattern.length() > (REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
+ && pattern.startsWith(REGEX_HANDLER_PREFIX) && pattern.endsWith(PATTERN_HANDLER_SUFFIX)) {
+ // FIXME: ICK! But we can't do partial matches for regex, so we have to reserve judgement until we have
+ // a file to deal with, or we can definitely say this is an exclusion...
+ return true;
+ } else {
+ if (pattern.length() > (ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
+ && pattern.startsWith(ANT_HANDLER_PREFIX) && pattern.endsWith(PATTERN_HANDLER_SUFFIX)) {
+ pattern =
+ pattern.substring(ANT_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length());
+ }
+
+ String altStr = str.replace('\\', '/');
+
+ return matchAntPathPatternStart(pattern, str, File.separator, isCaseSensitive)
+ || matchAntPathPatternStart(pattern, altStr, "/", isCaseSensitive);
+ }
+ }
+
+ private static boolean matchAntPathPatternStart(String pattern, String str, String separator, boolean isCaseSensitive) {
+ // When str starts with a File.separator, pattern has to start with a
+ // File.separator.
+ // When pattern starts with a File.separator, str has to start with a
+ // File.separator.
+ if (str.startsWith(separator) != pattern.startsWith(separator)) {
+ return false;
+ }
+
+ List<String> patDirs = tokenizePath(pattern, separator);
+ List<String> strDirs = tokenizePath(str, separator);
+
+ int patIdxStart = 0;
+ int patIdxEnd = patDirs.size() - 1;
+ int strIdxStart = 0;
+ int strIdxEnd = strDirs.size() - 1;
+
+ // up to first '**'
+ while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+ String patDir = patDirs.get(patIdxStart);
+ if (patDir.equals("**")) {
+ break;
+ }
+ if (!match(patDir, strDirs.get(strIdxStart),
+ isCaseSensitive)) {
+ return false;
+ }
+ patIdxStart++;
+ strIdxStart++;
+ }
+
+ // CHECKSTYLE:OFF
+ if (strIdxStart > strIdxEnd) {
+ // String is exhausted
+ return true;
+ } else {
+ return patIdxStart <= patIdxEnd;
+ }
+ // CHECKSTYLE:ON
+ }
+
+ /**
+ * Tests whether or not a given path matches a given pattern.
+ *
+ * @param pattern The pattern to match against. Must not be
+ * {@code null}.
+ * @param str The path to match, as a String. Must not be
+ * {@code null}.
+ * @return <code>true</code> if the pattern matches against the string,
+ * or <code>false</code> otherwise.
+ */
+ public static boolean matchPath(String pattern, String str) {
+ return matchPath(pattern, str, true);
+ }
+
+ /**
+ * Tests whether or not a given path matches a given pattern.
+ *
+ * @param pattern The pattern to match against. Must not be
+ * {@code null}.
+ * @param str The path to match, as a String. Must not be
+ * {@code null}.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ * @return <code>true</code> if the pattern matches against the string,
+ * or <code>false</code> otherwise.
+ */
+ public static boolean matchPath(String pattern, String str, boolean isCaseSensitive) {
+ if (pattern.length() > (REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
+ && pattern.startsWith(REGEX_HANDLER_PREFIX) && pattern.endsWith(PATTERN_HANDLER_SUFFIX)) {
+ pattern = pattern.substring(REGEX_HANDLER_PREFIX.length(), pattern.length()
+ - PATTERN_HANDLER_SUFFIX.length());
+
+ return str.matches(pattern);
+ } else {
+ if (pattern.length() > (ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
+ && pattern.startsWith(ANT_HANDLER_PREFIX) && pattern.endsWith(PATTERN_HANDLER_SUFFIX)) {
+ pattern =
+ pattern.substring(ANT_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length());
+ }
+
+ return matchAntPathPattern(pattern, str, isCaseSensitive);
+ }
+ }
+
+ private static boolean matchAntPathPattern(String pattern, String str, boolean isCaseSensitive) {
+ // When str starts with a File.separator, pattern has to start with a
+ // File.separator.
+ // When pattern starts with a File.separator, str has to start with a
+ // File.separator.
+ if (str.startsWith(File.separator) != pattern.startsWith(File.separator)) {
+ return false;
+ }
+
+ List<String> patDirs = tokenizePath(pattern, File.separator);
+ List<String> strDirs = tokenizePath(str, File.separator);
+
+ int patIdxStart = 0;
+ int patIdxEnd = patDirs.size() - 1;
+ int strIdxStart = 0;
+ int strIdxEnd = strDirs.size() - 1;
+
+ // up to first '**'
+ while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+ String patDir = patDirs.get(patIdxStart);
+ if (patDir.equals("**")) {
+ break;
+ }
+ if (!match(patDir, strDirs.get(strIdxStart),
+ isCaseSensitive)) {
+ patDirs = null;
+ strDirs = null;
+ return false;
+ }
+ patIdxStart++;
+ strIdxStart++;
+ }
+ if (strIdxStart > strIdxEnd) {
+ // String is exhausted
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (!patDirs.get(i).equals("**")) {
+ patDirs = null;
+ strDirs = null;
+ return false;
+ }
+ }
+ return true;
+ } else {
+ if (patIdxStart > patIdxEnd) {
+ // String not exhausted, but pattern is. Failure.
+ patDirs = null;
+ strDirs = null;
+ return false;
+ }
+ }
+
+ // up to last '**'
+ while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+ String patDir = patDirs.get(patIdxEnd);
+ if (patDir.equals("**")) {
+ break;
+ }
+ if (!match(patDir, strDirs.get(strIdxEnd),
+ isCaseSensitive)) {
+ patDirs = null;
+ strDirs = null;
+ return false;
+ }
+ patIdxEnd--;
+ strIdxEnd--;
+ }
+ if (strIdxStart > strIdxEnd) {
+ // String is exhausted
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (!patDirs.get(i).equals("**")) {
+ patDirs = null;
+ strDirs = null;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
+ int patIdxTmp = -1;
+ for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
+ if (patDirs.get(i).equals("**")) {
+ patIdxTmp = i;
+ break;
+ }
+ }
+ if (patIdxTmp == patIdxStart + 1) {
+ // '**/**' situation, so skip one
+ patIdxStart++;
+ continue;
+ }
+ // Find the pattern between padIdxStart & padIdxTmp in str between
+ // strIdxStart & strIdxEnd
+ int patLength = patIdxTmp - patIdxStart - 1;
+ int strLength = strIdxEnd - strIdxStart + 1;
+ int foundIdx = -1;
+ strLoop:
+ for (int i = 0; i <= strLength - patLength; i++) {
+ for (int j = 0; j < patLength; j++) {
+ String subPat = patDirs.get(patIdxStart + j + 1);
+ String subStr = strDirs.get(strIdxStart + i + j);
+ if (!match(subPat, subStr, isCaseSensitive)) {
+ continue strLoop;
+ }
+ }
+
+ foundIdx = strIdxStart + i;
+ break;
+ }
+
+ if (foundIdx == -1) {
+ patDirs = null;
+ strDirs = null;
+ return false;
+ }
+
+ patIdxStart = patIdxTmp;
+ strIdxStart = foundIdx + patLength;
+ }
+
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (!patDirs.get(i).equals("**")) {
+ patDirs = null;
+ strDirs = null;
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Tests whether or not a string matches against a pattern.
+ * The pattern may contain two special characters:<br>
+ * '*' means zero or more characters<br>
+ * '?' means one and only one character
+ *
+ * @param pattern The pattern to match against.
+ * Must not be {@code null}.
+ * @param str The string which must be matched against the pattern.
+ * Must not be {@code null}.
+ * @return <code>true</code> if the string matches against the pattern,
+ * or <code>false</code> otherwise.
+ */
+ public static boolean match(String pattern, String str) {
+ return match(pattern, str, true);
+ }
+
+ /**
+ * Tests whether or not a string matches against a pattern.
+ * The pattern may contain two special characters:<br>
+ * '*' means zero or more characters<br>
+ * '?' means one and only one character
+ *
+ * @param pattern The pattern to match against.
+ * Must not be {@code null}.
+ * @param str The string which must be matched against the pattern.
+ * Must not be {@code null}.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ * @return <code>true</code> if the string matches against the pattern,
+ * or <code>false</code> otherwise.
+ */
+ @SuppressWarnings("PMD.AssignmentInOperand")
+ public static boolean match(String pattern, String str, boolean isCaseSensitive) {
+ char[] patArr = pattern.toCharArray();
+ char[] strArr = str.toCharArray();
+ int patIdxStart = 0;
+ int patIdxEnd = patArr.length - 1;
+ int strIdxStart = 0;
+ int strIdxEnd = strArr.length - 1;
+ char ch;
+
+ boolean containsStar = false;
+ for (char aPatArr : patArr) {
+ if (aPatArr == '*') {
+ containsStar = true;
+ break;
+ }
+ }
+
+ if (!containsStar) {
+ // No '*'s, so we make a shortcut
+ if (patIdxEnd != strIdxEnd) {
+ return false; // Pattern and string do not have the same size
+ }
+ for (int i = 0; i <= patIdxEnd; i++) {
+ ch = patArr[i];
+ if ((ch != '?') && (!equals(ch, strArr[i], isCaseSensitive))) {
+ return false; // Character mismatch
+ }
+ }
+ return true; // String matches against pattern
+ }
+
+ if (patIdxEnd == 0) {
+ return true; // Pattern contains only '*', which matches anything
+ }
+
+ // Process characters before first star
+ // CHECKSTYLE:OFF
+ while (((ch = patArr[patIdxStart]) != '*') && (strIdxStart <= strIdxEnd)) {
+ if ((ch != '?') && (!equals(ch, strArr[strIdxStart], isCaseSensitive))) {
+ return false; // Character mismatch
+ }
+ patIdxStart++;
+ strIdxStart++;
+ }
+ // CHECKSTYLE:ON
+
+ if (strIdxStart > strIdxEnd) {
+ // All characters in the string are used. Check if only '*'s are
+ // left in the pattern. If so, we succeeded. Otherwise failure.
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (patArr[i] != '*') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Process characters after last star
+ // CHECKSTYLE:OFF
+ while (((ch = patArr[patIdxEnd]) != '*') && (strIdxStart <= strIdxEnd)) {
+ if ((ch != '?') && (!equals(ch, strArr[strIdxEnd], isCaseSensitive))) {
+ return false; // Character mismatch
+ }
+ patIdxEnd--;
+ strIdxEnd--;
+ }
+ // CHECKSTYLE:ON
+
+ if (strIdxStart > strIdxEnd) {
+ // All characters in the string are used. Check if only '*'s are
+ // left in the pattern. If so, we succeeded. Otherwise failure.
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (patArr[i] != '*') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // process pattern between stars. padIdxStart and patIdxEnd point always to a '*'.
+ while ((patIdxStart != patIdxEnd) && (strIdxStart <= strIdxEnd)) {
+ int patIdxTmp = -1;
+ for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
+ if (patArr[i] == '*') {
+ patIdxTmp = i;
+ break;
+ }
+ }
+ if (patIdxTmp == patIdxStart + 1) {
+ // Two stars next to each other, skip the first one.
+ patIdxStart++;
+ continue;
+ }
+ // Find the pattern between padIdxStart & padIdxTmp in str between
+ // strIdxStart & strIdxEnd
+ int patLength = patIdxTmp - patIdxStart - 1;
+ int strLength = strIdxEnd - strIdxStart + 1;
+ int foundIdx = -1;
+ strLoop:
+ for (int i = 0; i <= strLength - patLength; i++) {
+ for (int j = 0; j < patLength; j++) {
+ ch = patArr[patIdxStart + j + 1];
+ if (ch != '?' && !equals(ch, strArr[strIdxStart + i + j], isCaseSensitive)) {
+ continue strLoop;
+ }
+ }
+
+ foundIdx = strIdxStart + i;
+ break;
+ }
+
+ if (foundIdx == -1) {
+ return false;
+ }
+
+ patIdxStart = patIdxTmp;
+ strIdxStart = foundIdx + patLength;
+ }
+
+ // All characters in the string are used. Check if only '*'s are left
+ // in the pattern. If so, we succeeded. Otherwise failure.
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (patArr[i] != '*') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Tests whether two characters are equal.
+ * @param c1 1st character
+ * @param c2 2nd character
+ * @param isCaseSensitive Whether to compare case sensitive
+ * @return {@code true} if equal characters
+ */
+ public static boolean equals(char c1, char c2, boolean isCaseSensitive) {
+ if (c1 == c2) {
+ return true;
+ }
+ if (!isCaseSensitive) {
+ // NOTE: Try both upper case and lower case as done by String.equalsIgnoreCase()
+ if (Character.toUpperCase(c1) == Character.toUpperCase(c2)
+ || Character.toLowerCase(c1) == Character.toLowerCase(c2)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Breaks a path up into a Vector of path elements, tokenizing on
+ * <code>File.separator</code>.
+ *
+ * @param path Path to tokenize. Must not be {@code null}.
+ * @return a List of path elements from the tokenized path
+ */
+ public static List<String> tokenizePath(String path) {
+ return tokenizePath(path, File.separator);
+ }
+
+ public static List<String> tokenizePath(String path, String separator) {
+ List<String> ret = new ArrayList<>();
+ StringTokenizer st = new StringTokenizer(path, separator);
+ while (st.hasMoreTokens()) {
+ ret.add(st.nextToken());
+ }
+ return ret;
+ }
+
+ /** /**
+ * Converts a path to one matching the target file system by applying the
+ * "slashification" rules, converting it to a local path and
+ * then translating its separator to the target file system one (if different
+ * than local one)
+ * @param path The input path
+ * @param pathSeparator The separator used to build the input path
+ * @param fs The target {@link FileSystem} - may not be {@code null}
+ * @return The transformed path
+ * @see #translateToLocalFileSystemPath(String, char, String)
+ */
+ public static String translateToLocalFileSystemPath(String path, char pathSeparator, FileSystem fs) {
+ return translateToLocalFileSystemPath(path, pathSeparator, Objects.requireNonNull(fs, "No target file system").getSeparator());
+ }
+
+ /**
+ * Converts a path to one matching the target file system by applying the
+ * "slashification" rules, converting it to a local path and
+ * then translating its separator to the target file system one (if different
+ * than local one)
+ * @param path The input path
+ * @param pathSeparator The separator used to build the input path
+ * @param fsSeparator The target file system separator
+ * @return The transformed path
+ * @see #applySlashifyRules(String, char)
+ * @see #translateToLocalPath(String)
+ * @see #translateToFileSystemPath(String, String, String)
+ */
+ public static String translateToLocalFileSystemPath(String path, char pathSeparator, String fsSeparator) {
+ // In case double slashes and other patterns are used
+ String slashified = applySlashifyRules(path, pathSeparator);
+ // In case we are running on Windows
+ String localPath = translateToLocalPath(slashified);
+ return translateToFileSystemPath(localPath, File.separator, fsSeparator);
+ }
+
+ /**
+ * Applies the "slashification" rules as specified in
+ * <A HREF="http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_266">Single Unix Specification version 3, section 3.266</A>
+ * and <A HREF="http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11">section 4.11 - Pathname resolution</A>
+ * @param path The original path - ignored if {@code null}/empty or does
+ * not contain any slashes
+ * @param sepChar The "slash" character
+ * @return The effective path - may be same as input if no changes required
+ */
+ public static String applySlashifyRules(String path, char sepChar) {
+ if (GenericUtils.isEmpty(path)) {
+ return path;
+ }
+
+ int curPos = path.indexOf(sepChar);
+ if (curPos < 0) {
+ return path; // no slashes to handle
+ }
+
+ int lastPos = 0;
+ StringBuilder sb = null;
+ while (curPos < path.length()) {
+ curPos++; // skip the 1st '/'
+
+ /*
+ * As per Single Unix Specification version 3, section 3.266:
+ *
+ * Multiple successive slashes are considered to be the
+ * same as one slash
+ */
+ int nextPos = curPos;
+ while ((nextPos < path.length()) && (path.charAt(nextPos) == sepChar)) {
+ nextPos++;
+ }
+
+ /*
+ * At this stage, nextPos is the first non-slash character after a
+ * possibly 'seqLen' sequence of consecutive slashes.
+ */
+ int seqLen = nextPos - curPos;
+ if (seqLen > 0) {
+ if (sb == null) {
+ sb = new StringBuilder(path.length() - seqLen);
+ }
+
+ if (lastPos < curPos) {
+ String clrText = path.substring(lastPos, curPos);
+ sb.append(clrText);
+ }
+
+ lastPos = nextPos;
+ }
+
+ if (nextPos >= path.length()) {
+ break; // no more data
+ }
+
+ curPos = path.indexOf(sepChar, nextPos);
+ if (curPos < nextPos) {
+ break; // no more slashes
+ }
+ }
+
+ // check if any leftovers for the modified path
+ if (sb != null) {
+ if (lastPos < path.length()) {
+ String clrText = path.substring(lastPos);
+ sb.append(clrText);
+ }
+
+ path = sb.toString();
+ }
+
+ /*
+ * At this point we know for sure that 'path' contains only SINGLE
+ * slashes. According to section 4.11 - Pathname resolution
+ *
+ * A pathname that contains at least one non-slash character
+ * and that ends with one or more trailing slashes shall be
+ * resolved as if a single dot character ( '.' ) were appended
+ * to the pathname.
+ */
+ if ((path.length() > 1) && (path.charAt(path.length() - 1) == sepChar)) {
+ return path + ".";
+ } else {
+ return path;
+ }
+ }
+
+ /**
+ * Converts a possibly '/' separated path to a local path. <B>Note:</B>
+ * takes special care of Windows drive paths - e.g., {@code C:}
+ * by converting them to "C:\"
+ *
+ * @param path The original path - ignored if {@code null}/empty
+ * @return The local path
+ */
+ public static String translateToLocalPath(String path) {
+ if (GenericUtils.isEmpty(path) || (File.separatorChar == '/')) {
+ return path;
+ }
+
+ // This code is reached if we are running on Windows
+ String localPath = path.replace('/', File.separatorChar);
+ // check if '/c:' prefix
+ if ((localPath.charAt(0) == File.separatorChar) && isWindowsDriveSpecified(localPath, 1, localPath.length() - 1)) {
+ localPath = localPath.substring(1);
+ }
+ if (!isWindowsDriveSpecified(localPath)) {
+ return localPath; // assume a relative path
+ }
+
+ /*
+ * Here we know that we have at least a "C:" string - make sure it
+ * is followed by the local file separator. Note: if all we have is
+ * just the drive, we will create a "C:\" path since this is the
+ * preferred Windows way to refer to root drives in the file system
+ */
+ if (localPath.length() == 2) {
+ return localPath + File.separator; // all we have is "C:"
+ } else if (localPath.charAt(2) != File.separatorChar) {
+ // be nice and add the missing file separator - C:foo => C:\foo
+ return localPath.substring(0, 2) + File.separator + localPath.substring(2);
+ } else {
+ return localPath;
+ }
+ }
+
+ public static boolean isWindowsDriveSpecified(CharSequence cs) {
+ return isWindowsDriveSpecified(cs, 0, GenericUtils.length(cs));
+ }
+
+ public static boolean isWindowsDriveSpecified(CharSequence cs, int offset, int len) {
+ if ((len < 2) || (cs.charAt(offset + 1) != ':')) {
+ return false;
+ }
+
+ char drive = cs.charAt(offset);
+ return ((drive >= 'a') && (drive <= 'z')) || ((drive >= 'A') && (drive <= 'Z'));
+ }
+
+ /**
+ * Converts a path containing a specific separator to one using the
+ * specified file-system one
+ * @param path The input path - ignored if {@code null}/empty
+ * @param pathSeparator The separator used to build the input path - may not
+ * be {@code null}/empty
+ * @param fs The target {@link FileSystem} - may not be {@code null}
+ * @return The path where the separator used to build it is replaced by
+ * the file-system one (if different)
+ * @see FileSystem#getSeparator()
+ * @see #translateToFileSystemPath(String, String, String)
+ */
+ public static String translateToFileSystemPath(String path, String pathSeparator, FileSystem fs) {
+ return translateToFileSystemPath(path, pathSeparator, Objects.requireNonNull(fs, "No target file system").getSeparator());
+ }
+
+ /**
+ * Converts a path containing a specific separator to one using the
+ * specified file-system one
+ * @param path The input path - ignored if {@code null}/empty
+ * @param pathSeparator The separator used to build the input path - may not
+ * be {@code null}/empty
+ * @param fsSeparator The target file system separator - may not be {@code null}/empty
+ * @return The path where the separator used to build it is replaced by
+ * the file-system one (if different)
+ * @throws IllegalArgumentException if path or file-system separator are {@code null}/empty
+ * or if the separators are different and the path contains the target
+ * file-system separator as it would create an ambiguity
+ */
+ public static String translateToFileSystemPath(String path, String pathSeparator, String fsSeparator) {
+ ValidateUtils.checkNotNullAndNotEmpty(pathSeparator, "Missing path separator");
+ ValidateUtils.checkNotNullAndNotEmpty(fsSeparator, "Missing file-system separator");
+
+ if (GenericUtils.isEmpty(path) || Objects.equals(pathSeparator, fsSeparator)) {
+ return path;
+ }
+
+ // make sure path does not contain the target separator
+ if (path.contains(fsSeparator)) {
+ ValidateUtils.throwIllegalArgumentException("File system replacement may yield ambiguous result for %s with separator=%s", path, fsSeparator);
+ }
+
+ // check most likely case
+ if ((pathSeparator.length() == 1) && (fsSeparator.length() == 1)) {
+ return path.replace(pathSeparator.charAt(0), fsSeparator.charAt(0));
+ } else {
+ return path.replace(pathSeparator, fsSeparator);
+ }
+ }
+
+ /**
+ * Returns dependency information on these two files. If src has been
+ * modified later than target, it returns true. If target doesn't exist,
+ * it likewise returns true. Otherwise, target is newer than src and
+ * is not out of date, thus the method returns false. It also returns
+ * false if the src file doesn't even exist, since how could the
+ * target then be out of date.
+ *
+ * @param src the original file
+ * @param target the file being compared against
+ * @param granularity the amount in seconds of slack we will give in
+ * determining out of dateness
+ * @return whether the target is out of date
+ */
+ public static boolean isOutOfDate(File src, File target, int granularity) {
+ if (!src.exists()) {
+ return false;
+ }
+ if (!target.exists()) {
+ return true;
+ }
+ return (src.lastModified() - granularity) > target.lastModified();
+ }
+
+ /**
+ * "Flattens" a string by removing all whitespace (space, tab, linefeed,
+ * carriage return, and formfeed). This uses StringTokenizer and the
+ * default set of tokens as documented in the single arguement constructor.
+ *
+ * @param input a String to remove all whitespace.
+ * @return a String that has had all whitespace removed.
+ */
+ public static String removeWhitespace(String input) {
+ StringBuilder result = new StringBuilder();
+ if (input != null) {
+ StringTokenizer st = new StringTokenizer(input);
+ while (st.hasMoreTokens()) {
+ result.append(st.nextToken());
+ }
+ }
+ return result.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/SshdEventListener.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/SshdEventListener.java b/sshd-common/src/main/java/org/apache/sshd/common/util/SshdEventListener.java
new file mode 100644
index 0000000..ea6a3dd
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/SshdEventListener.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.lang.reflect.Proxy;
+import java.util.EventListener;
+import java.util.Objects;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface SshdEventListener extends EventListener {
+
+ /**
+ * Makes sure that the listener is neither {@code null} nor a proxy
+ *
+ * @param <L> Type of {@link SshdEventListener} being validation
+ * @param listener The listener instance
+ * @param prefix Prefix text to be prepended to validation failure messages
+ * @return The validated instance
+ */
+ static <L extends SshdEventListener> L validateListener(L listener, String prefix) {
+ Objects.requireNonNull(listener, prefix + ": no instance");
+ ValidateUtils.checkTrue(!Proxy.isProxyClass(listener.getClass()), prefix + ": proxies N/A");
+ return listener;
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/ValidateUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
new file mode 100644
index 0000000..a55e5d6
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.function.Function;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class ValidateUtils {
+ private ValidateUtils() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ public static <T> T checkNotNull(T t, String message) {
+ checkTrue(t != null, message);
+ return t;
+ }
+
+ public static <T> T checkNotNull(T t, String message, Object arg) {
+ checkTrue(t != null, message, arg);
+ return t;
+ }
+
+ public static <T> T checkNotNull(T t, String message, long value) {
+ checkTrue(t != null, message, value);
+ return t;
+ }
+
+ public static <T> T checkNotNull(T t, String message, Object... args) {
+ checkTrue(t != null, message, args);
+ return t;
+ }
+
+ public static String checkNotNullAndNotEmpty(String t, String message) {
+ t = checkNotNull(t, message).trim();
+ checkTrue(GenericUtils.length(t) > 0, message);
+ return t;
+ }
+
+ public static String checkNotNullAndNotEmpty(String t, String message, Object arg) {
+ t = checkNotNull(t, message, arg).trim();
+ checkTrue(GenericUtils.length(t) > 0, message, arg);
+ return t;
+ }
+
+ public static String checkNotNullAndNotEmpty(String t, String message, Object... args) {
+ t = checkNotNull(t, message, args).trim();
+ checkTrue(GenericUtils.length(t) > 0, message, args);
+ return t;
+ }
+
+ public static <K, V, M extends Map<K, V>> M checkNotNullAndNotEmpty(M t, String message, Object... args) {
+ t = checkNotNull(t, message, args);
+ checkTrue(GenericUtils.size(t) > 0, message, args);
+ return t;
+ }
+
+ public static <T, C extends Collection<T>> C checkNotNullAndNotEmpty(C t, String message, Object... args) {
+ t = checkNotNull(t, message, args);
+ checkTrue(GenericUtils.size(t) > 0, message, args);
+ return t;
+ }
+
+ public static <T, C extends Iterable<T>> C checkNotNullAndNotEmpty(C t, String message, Object... args) {
+ t = checkNotNull(t, message, args);
+ checkTrue(GenericUtils.isNotEmpty(t), message, args);
+ return t;
+ }
+
+ public static byte[] checkNotNullAndNotEmpty(byte[] a, String message) {
+ a = checkNotNull(a, message);
+ checkTrue(NumberUtils.length(a) > 0, message);
+ return a;
+ }
+
+ public static byte[] checkNotNullAndNotEmpty(byte[] a, String message, Object... args) {
+ a = checkNotNull(a, message, args);
+ checkTrue(NumberUtils.length(a) > 0, message, args);
+ return a;
+ }
+
+ public static char[] checkNotNullAndNotEmpty(char[] a, String message) {
+ a = checkNotNull(a, message);
+ checkTrue(GenericUtils.length(a) > 0, message);
+ return a;
+ }
+
+ public static char[] checkNotNullAndNotEmpty(char[] a, String message, Object... args) {
+ a = checkNotNull(a, message, args);
+ checkTrue(GenericUtils.length(a) > 0, message, args);
+ return a;
+ }
+
+ public static int[] checkNotNullAndNotEmpty(int[] a, String message) {
+ a = checkNotNull(a, message);
+ checkTrue(NumberUtils.length(a) > 0, message);
+ return a;
+ }
+
+ public static int[] checkNotNullAndNotEmpty(int[] a, String message, Object... args) {
+ a = checkNotNull(a, message, args);
+ checkTrue(NumberUtils.length(a) > 0, message, args);
+ return a;
+ }
+
+ public static <T> T[] checkNotNullAndNotEmpty(T[] t, String message, Object... args) {
+ t = checkNotNull(t, message, args);
+ checkTrue(GenericUtils.length(t) > 0, message, args);
+ return t;
+ }
+
+ public static <T> T checkInstanceOf(Object v, Class<T> expected, String message, long value) {
+ Class<?> actual = checkNotNull(v, message, value).getClass();
+ checkTrue(expected.isAssignableFrom(actual), message, value);
+ return expected.cast(v);
+ }
+
+ public static <T> T checkInstanceOf(Object v, Class<T> expected, String message) {
+ return checkInstanceOf(v, expected, message, GenericUtils.EMPTY_OBJECT_ARRAY);
+ }
+
+ public static <T> T checkInstanceOf(Object v, Class<T> expected, String message, Object arg) {
+ Class<?> actual = checkNotNull(v, message, arg).getClass();
+ checkTrue(expected.isAssignableFrom(actual), message, arg);
+ return expected.cast(v);
+ }
+
+ public static <T> T checkInstanceOf(Object v, Class<T> expected, String message, Object... args) {
+ Class<?> actual = checkNotNull(v, message, args).getClass();
+ checkTrue(expected.isAssignableFrom(actual), message, args);
+ return expected.cast(v);
+ }
+
+ public static void checkTrue(boolean flag, String message) {
+ if (!flag) {
+ throwIllegalArgumentException(message, GenericUtils.EMPTY_OBJECT_ARRAY);
+ }
+ }
+
+ public static void checkTrue(boolean flag, String message, long value) {
+ if (!flag) {
+ throwIllegalArgumentException(message, value);
+ }
+ }
+
+ public static void checkTrue(boolean flag, String message, Object arg) {
+ if (!flag) {
+ throwIllegalArgumentException(message, arg);
+ }
+ }
+
+ public static void checkTrue(boolean flag, String message, Object... args) {
+ if (!flag) {
+ throwIllegalArgumentException(message, args);
+ }
+ }
+
+ public static void throwIllegalArgumentException(String format, Object... args) {
+ throw createFormattedException(IllegalArgumentException::new, format, args);
+ }
+
+ public static void checkState(boolean flag, String message) {
+ if (!flag) {
+ throwIllegalStateException(message, GenericUtils.EMPTY_OBJECT_ARRAY);
+ }
+ }
+
+ public static void checkState(boolean flag, String message, long value) {
+ if (!flag) {
+ throwIllegalStateException(message, value);
+ }
+ }
+
+ public static void checkState(boolean flag, String message, Object arg) {
+ if (!flag) {
+ throwIllegalStateException(message, arg);
+ }
+ }
+
+ public static void checkState(boolean flag, String message, Object... args) {
+ if (!flag) {
+ throwIllegalStateException(message, args);
+ }
+ }
+
+ public static void throwIllegalStateException(String format, Object... args) {
+ throw createFormattedException(IllegalStateException::new, format, args);
+ }
+
+ public static <T extends Throwable> T createFormattedException(
+ Function<? super String, ? extends T> constructor, String format, Object... args) {
+ String message = String.format(format, args);
+ return constructor.apply(message);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/VersionInfo.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/VersionInfo.java b/sshd-common/src/main/java/org/apache/sshd/common/util/VersionInfo.java
new file mode 100644
index 0000000..ed1aab7
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/VersionInfo.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class VersionInfo implements Serializable, Comparable<VersionInfo> {
+ private static final long serialVersionUID = -9127482432228413836L;
+
+ private final int majorVersion;
+ private final int minorVersion;
+ private final int release;
+ private final int buildNumber;
+
+ public VersionInfo(int major, int minor) {
+ this(major, minor, 0, 0);
+ }
+
+ public VersionInfo(int major, int minor, int release, int build) {
+ this.majorVersion = major;
+ this.minorVersion = minor;
+ this.release = release;
+ this.buildNumber = build;
+ }
+
+ public final int getMajorVersion() {
+ return majorVersion;
+ }
+
+ public final int getMinorVersion() {
+ return minorVersion;
+ }
+
+ public final int getRelease() {
+ return release;
+ }
+
+ public final int getBuildNumber() {
+ return buildNumber;
+ }
+
+ @Override
+ public int hashCode() {
+ return NumberUtils.hashCode(getMajorVersion(), getMinorVersion(), getRelease(), getBuildNumber());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ return compareTo((VersionInfo) obj) == 0;
+ }
+
+ @Override
+ public int compareTo(VersionInfo o) {
+ if (o == null) {
+ return -1; // push nulls to end
+ }
+ if (o == this) {
+ return 0;
+ }
+
+ int nRes = Integer.compare(getMajorVersion(), o.getMajorVersion());
+ if (nRes == 0) {
+ nRes = Integer.compare(getMinorVersion(), o.getMinorVersion());
+ }
+ if (nRes == 0) {
+ nRes = Integer.compare(getRelease(), o.getRelease());
+ }
+ if (nRes == 0) {
+ nRes = Integer.compare(getBuildNumber(), o.getBuildNumber());
+ }
+
+ return nRes;
+ }
+
+ @Override
+ public String toString() {
+ return NumberUtils.join('.', getMajorVersion(), getMinorVersion(), getRelease(), getBuildNumber());
+ }
+
+ /**
+ * Parses a version string - assumed to contain at most 4 non-negative
+ * components separated by a '.'. If less than 4 components are found, then
+ * the rest are assumed to be zero. If more than 4 components found, then
+ * only the 1st ones are parsed.
+ *
+ * @param version The version string - ignored if {@code null}/empty
+ * @return The parsed {@link VersionInfo} - or {@code null} if empty input
+ * @throws NumberFormatException If failed to parse any of the components
+ * @throws IllegalArgumentException If any of the parsed components is negative
+ */
+ public static VersionInfo parse(String version) throws NumberFormatException {
+ String[] comps = GenericUtils.split(version, '.');
+ if (GenericUtils.isEmpty(comps)) {
+ return null;
+ }
+
+ int[] values = new int[4];
+ int maxValues = Math.min(comps.length, values.length);
+ for (int index = 0; index < maxValues; index++) {
+ String c = comps[index];
+ int v = Integer.parseInt(c);
+ ValidateUtils.checkTrue(v >= 0, "Invalid version component in %s", version);
+ values[index] = v;
+ }
+
+ return new VersionInfo(values[0], values[1], values[2], values[3]);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
new file mode 100644
index 0000000..dd61473
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
@@ -0,0 +1,798 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.buffer;
+
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.IntUnaryOperator;
+import java.util.logging.Level;
+
+import org.apache.sshd.common.PropertyResolver;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.Readable;
+import org.apache.sshd.common.util.buffer.keys.BufferPublicKeyParser;
+import org.apache.sshd.common.util.logging.SimplifiedLog;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Provides an abstract message buffer for encoding SSH messages
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class Buffer implements Readable {
+ protected final byte[] workBuf = new byte[Long.BYTES];
+
+ protected Buffer() {
+ super();
+ }
+
+ public abstract int rpos();
+
+ public abstract void rpos(int rpos);
+
+ public abstract int wpos();
+
+ public abstract void wpos(int wpos);
+
+ public abstract int capacity();
+
+ public abstract byte[] array();
+
+ public abstract void compact();
+
+ public byte[] getCompactData() {
+ int l = available();
+ if (l > 0) {
+ byte[] b = new byte[l];
+ System.arraycopy(array(), rpos(), b, 0, l);
+ return b;
+ } else {
+ return GenericUtils.EMPTY_BYTE_ARRAY;
+ }
+ }
+
+ public void clear() {
+ clear(true);
+ }
+
+ public abstract void clear(boolean wipeData);
+
+ public boolean isValidMessageStructure(Class<?>... fieldTypes) {
+ return isValidMessageStructure(GenericUtils.isEmpty(fieldTypes) ? Collections.emptyList() : Arrays.asList(fieldTypes));
+ }
+
+ public boolean isValidMessageStructure(Collection<Class<?>> fieldTypes) {
+ if (GenericUtils.isEmpty(fieldTypes)) {
+ return true;
+ }
+
+ int remainLen = available();
+ int readOffset = 0;
+ for (Class<?> ft : fieldTypes) {
+ if ((ft == boolean.class) || (ft == Boolean.class)
+ || (ft == byte.class) || (ft == Byte.class)) {
+ if (remainLen < Byte.BYTES) {
+ return false;
+ }
+
+ remainLen -= Byte.BYTES;
+ readOffset += Byte.BYTES;
+ } else if ((ft == short.class) || (ft == Short.class)) {
+ if (remainLen < Short.BYTES) {
+ return false;
+ }
+
+ remainLen -= Short.BYTES;
+ readOffset += Short.BYTES;
+ } else if ((ft == int.class) || (ft == Integer.class)) {
+ if (remainLen < Integer.BYTES) {
+ return false;
+ }
+
+ remainLen -= Integer.BYTES;
+ readOffset += Integer.BYTES;
+ } else if ((ft == long.class) || (ft == Long.class)) {
+ if (remainLen < Long.BYTES) {
+ return false;
+ }
+
+ remainLen -= Long.BYTES;
+ readOffset += Long.BYTES;
+ } else if ((ft == byte[].class) || (ft == String.class)) {
+ if (remainLen < Integer.BYTES) {
+ return false;
+ }
+
+ copyRawBytes(readOffset, workBuf, 0, Integer.BYTES);
+ remainLen -= Integer.BYTES;
+ readOffset += Integer.BYTES;
+
+ long length = BufferUtils.getUInt(workBuf, 0, Integer.BYTES);
+ if (length > remainLen) {
+ return false;
+ }
+
+ remainLen -= (int) length;
+ readOffset += (int) length;
+ }
+ }
+
+ return true;
+ }
+
+ protected abstract void copyRawBytes(int offset, byte[] buf, int pos, int len);
+
+ public String toHex() {
+ return BufferUtils.toHex(array(), rpos(), available());
+ }
+
+ public void dumpHex(SimplifiedLog logger, String prefix, PropertyResolver resolver) {
+ dumpHex(logger, BufferUtils.DEFAULT_HEXDUMP_LEVEL, prefix, resolver);
+ }
+
+ public void dumpHex(SimplifiedLog logger, Level level, String prefix, PropertyResolver resolver) {
+ BufferUtils.dumpHex(logger, level, prefix, resolver, BufferUtils.DEFAULT_HEX_SEPARATOR, array(), rpos(), available());
+ }
+
+ /*======================
+ Read methods
+ ======================*/
+
+ public int getUByte() {
+ return getByte() & 0xFF;
+ }
+
+ public byte getByte() {
+ ensureAvailable(Byte.BYTES);
+ getRawBytes(workBuf, 0, Byte.BYTES);
+ return workBuf[0];
+ }
+
+ public short getShort() {
+ ensureAvailable(Short.BYTES);
+ getRawBytes(workBuf, 0, Short.BYTES);
+ short v = (short) ((workBuf[1] << Byte.SIZE) & 0xFF00);
+ v |= (short) (workBuf[0] & 0xF);
+ return v;
+ }
+
+ public int getInt() {
+ return (int) getUInt();
+ }
+
+ public long getUInt() {
+ ensureAvailable(Integer.BYTES);
+ getRawBytes(workBuf, 0, Integer.BYTES);
+ return BufferUtils.getUInt(workBuf, 0, Integer.BYTES);
+ }
+
+ public long getLong() {
+ ensureAvailable(Long.BYTES);
+ getRawBytes(workBuf, 0, Long.BYTES);
+ long l = ((long) workBuf[0] << 56) & 0xff00000000000000L;
+ l |= ((long) workBuf[1] << 48) & 0x00ff000000000000L;
+ l |= ((long) workBuf[2] << 40) & 0x0000ff0000000000L;
+ l |= ((long) workBuf[3] << 32) & 0x000000ff00000000L;
+ l |= ((long) workBuf[4] << 24) & 0x00000000ff000000L;
+ l |= ((long) workBuf[5] << 16) & 0x0000000000ff0000L;
+ l |= ((long) workBuf[6] << 8) & 0x000000000000ff00L;
+ l |= (workBuf[7]) & 0x00000000000000ffL;
+ return l;
+ }
+
+ @SuppressWarnings("PMD.BooleanGetMethodName")
+ public boolean getBoolean() {
+ return getByte() != 0;
+ }
+
+ public String getString() {
+ return getString(StandardCharsets.UTF_8);
+ }
+
+ /**
+ * @param usePrependedLength If {@code true} then there is a 32-bit
+ * value indicating the number of strings to read. Otherwise, the
+ * method will use a "greedy" reading of strings while more
+ * data available
+ * @return A {@link Collection} of the read strings
+ * @see #getStringList(boolean, Charset)
+ */
+ public Collection<String> getStringList(boolean usePrependedLength) {
+ return getStringList(usePrependedLength, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * @param usePrependedLength If {@code true} then there is a 32-bit
+ * value indicating the number of strings to read. Otherwise, the
+ * method will use a "greedy" reading of strings while more
+ * data available
+ * @param charset The {@link Charset} to use for the string
+ * @return A {@link Collection} of the read strings
+ * @see #getStringList(int, Charset)
+ * @see #getAvailableStrings()
+ */
+ public Collection<String> getStringList(boolean usePrependedLength, Charset charset) {
+ if (usePrependedLength) {
+ int count = getInt();
+ return getStringList(count, charset);
+ } else {
+ return getAvailableStrings(charset);
+ }
+ }
+
+ /**
+ * @return The remaining data as a list of strings
+ * @see #getAvailableStrings(Charset)
+ */
+ public Collection<String> getAvailableStrings() {
+ return getAvailableStrings(StandardCharsets.UTF_8);
+ }
+
+ /**
+ * @param charset The {@link Charset} to use for the strings
+ * @return The remaining data as a list of strings
+ * @see #available()
+ * @see #getString(Charset)
+ */
+ public Collection<String> getAvailableStrings(Charset charset) {
+ Collection<String> list = new LinkedList<>();
+ while (available() > 0) {
+ String s = getString(charset);
+ list.add(s);
+ }
+
+ return list;
+ }
+
+ /**
+ * @param count The <U>exact</U> number of strings to read - can be zero
+ * @return A {@link List} with the specified number of strings
+ * @see #getStringList(int, Charset)
+ */
+ public List<String> getStringList(int count) {
+ return getStringList(count, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * @param count The <U>exact</U> number of strings to read - can be zero
+ * @param charset The {@link Charset} of the strings
+ * @return A {@link List} with the specified number of strings
+ * @see #getString(Charset)
+ */
+ public List<String> getStringList(int count, Charset charset) {
+ if (count == 0) {
+ return Collections.emptyList();
+ }
+
+ List<String> list = new ArrayList<>(count);
+ for (int index = 0; index < count; index++) {
+ String s = getString(charset);
+ list.add(s);
+ }
+
+ return list;
+ }
+
+ public abstract String getString(Charset charset);
+
+ public BigInteger getMPInt() {
+ return new BigInteger(getMPIntAsBytes());
+ }
+
+ public byte[] getMPIntAsBytes() {
+ return getBytes();
+ }
+
+ public byte[] getBytes() {
+ int len = getInt();
+ if (len < 0) {
+ throw new BufferException("Bad item length: " + len);
+ }
+ ensureAvailable(len);
+ byte[] b = new byte[len];
+ getRawBytes(b);
+ return b;
+ }
+
+ public void getRawBytes(byte[] buf) {
+ getRawBytes(buf, 0, buf.length);
+ }
+
+ public PublicKey getPublicKey() throws SshException {
+ return getPublicKey(BufferPublicKeyParser.DEFAULT);
+ }
+
+ /**
+ * @param parser A {@link BufferPublicKeyParser} to extract the key from the buffer
+ * - never {@code null}
+ * @return The extracted {@link PublicKey} - may be {@code null} if the parser so decided
+ * @throws SshException If failed to extract the key
+ * @see #getRawPublicKey(BufferPublicKeyParser)
+ */
+ public PublicKey getPublicKey(BufferPublicKeyParser<? extends PublicKey> parser) throws SshException {
+ int ow = wpos();
+ int len = getInt();
+ wpos(rpos() + len);
+ try {
+ return getRawPublicKey(parser);
+ } finally {
+ wpos(ow);
+ }
+ }
+
+ public PublicKey getRawPublicKey() throws SshException {
+ return getRawPublicKey(BufferPublicKeyParser.DEFAULT);
+ }
+
+ /**
+ * @param parser A {@link BufferPublicKeyParser} to extract the key from the buffer
+ * - never {@code null}
+ * @return The extracted {@link PublicKey} - may be {@code null} if the parser so decided
+ * @throws SshException If failed to extract the key
+ */
+ public PublicKey getRawPublicKey(BufferPublicKeyParser<? extends PublicKey> parser) throws SshException {
+ Objects.requireNonNull(parser, "No key data parser");
+ try {
+ String keyType = getString();
+ if (!parser.isKeyTypeSupported(keyType)) {
+ throw new NoSuchAlgorithmException("Key type=" + keyType + ") not supported by parser=" + parser);
+ }
+
+ return parser.getRawPublicKey(keyType, this);
+ } catch (GeneralSecurityException e) {
+ throw new SshException(e);
+ }
+ }
+
+ public KeyPair getKeyPair() throws SshException {
+ try {
+ final PublicKey pub;
+ final PrivateKey prv;
+ final String keyAlg = getString();
+ if (KeyPairProvider.SSH_RSA.equals(keyAlg)) {
+ BigInteger e = getMPInt();
+ BigInteger n = getMPInt();
+ BigInteger d = getMPInt();
+ BigInteger qInv = getMPInt();
+ BigInteger q = getMPInt();
+ BigInteger p = getMPInt();
+ BigInteger dP = d.remainder(p.subtract(BigInteger.valueOf(1)));
+ BigInteger dQ = d.remainder(q.subtract(BigInteger.valueOf(1)));
+ KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
+ pub = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
+ prv = keyFactory.generatePrivate(new RSAPrivateCrtKeySpec(n, e, d, p, q, dP, dQ, qInv));
+ } else if (KeyPairProvider.SSH_DSS.equals(keyAlg)) {
+ BigInteger p = getMPInt();
+ BigInteger q = getMPInt();
+ BigInteger g = getMPInt();
+ BigInteger y = getMPInt();
+ BigInteger x = getMPInt();
+ KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
+ pub = keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
+ prv = keyFactory.generatePrivate(new DSAPrivateKeySpec(x, p, q, g));
+ } else if (KeyPairProvider.SSH_ED25519.equals(keyAlg)) {
+ return SecurityUtils.extractEDDSAKeyPair(this, keyAlg);
+ } else {
+ ECCurves curve = ECCurves.fromKeyType(keyAlg);
+ if (curve == null) {
+ throw new NoSuchAlgorithmException("Unsupported key pair algorithm: " + keyAlg);
+ }
+ String curveName = curve.getName();
+ ECParameterSpec params = curve.getParameters();
+ return extractEC(curveName, params);
+ }
+
+ return new KeyPair(pub, prv);
+ } catch (GeneralSecurityException e) {
+ throw new SshException(e);
+ }
+ }
+
+ protected KeyPair extractEC(String expectedCurveName, ECParameterSpec spec) throws GeneralSecurityException {
+ String curveName = getString();
+ if (!expectedCurveName.equals(curveName)) {
+ throw new InvalidKeySpecException("extractEC(" + expectedCurveName + ") mismatched curve name: " + curveName);
+ }
+
+ byte[] groupBytes = getBytes();
+ BigInteger exponent = getMPInt();
+
+ if (spec == null) {
+ throw new InvalidKeySpecException("extractEC(" + expectedCurveName + ") missing parameters for curve");
+ }
+
+ ECPoint group;
+ try {
+ group = ECCurves.octetStringToEcPoint(groupBytes);
+ } catch (RuntimeException e) {
+ throw new InvalidKeySpecException("extractEC(" + expectedCurveName + ")"
+ + " failed (" + e.getClass().getSimpleName() + ")"
+ + " to decode EC group for curve: " + e.getMessage(),
+ e);
+ }
+
+ KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
+ PublicKey pubKey = keyFactory.generatePublic(new ECPublicKeySpec(group, spec));
+ PrivateKey privKey = keyFactory.generatePrivate(new ECPrivateKeySpec(exponent, spec));
+ return new KeyPair(pubKey, privKey);
+ }
+
+ public void ensureAvailable(int reqLen) throws BufferException {
+ int availLen = available();
+ if (availLen < reqLen) {
+ throw new BufferException("Underflow: requested=" + reqLen + ", available=" + availLen);
+ }
+ }
+
+ /*======================
+ Write methods
+ ======================*/
+
+ public void putByte(byte b) {
+ ensureCapacity(Byte.BYTES);
+ workBuf[0] = b;
+ putRawBytes(workBuf, 0, Byte.BYTES);
+ }
+
+ public void putBuffer(Readable buffer) {
+ putBuffer(buffer, true);
+ }
+
+ public abstract int putBuffer(Readable buffer, boolean expand);
+
+ public abstract void putBuffer(ByteBuffer buffer);
+
+ /**
+ * Writes 16 bits
+ *
+ * @param i The 16-bit value
+ */
+ public void putShort(int i) {
+ ensureCapacity(Short.BYTES);
+ workBuf[0] = (byte) (i >> 8);
+ workBuf[1] = (byte) i;
+ putRawBytes(workBuf, 0, Short.BYTES);
+ }
+
+ /**
+ * Writes 32 bits
+ *
+ * @param i The 32-bit value
+ */
+ public void putInt(long i) {
+ BufferUtils.validateInt32Value(i, "Invalid 32-bit value: %d");
+ ensureCapacity(Integer.BYTES);
+ BufferUtils.putUInt(i, workBuf, 0, Integer.BYTES);
+ putRawBytes(workBuf, 0, Integer.BYTES);
+ }
+
+ /**
+ * Writes 64 bits
+ *
+ * @param i The 64-bit value
+ */
+ public void putLong(long i) {
+ ensureCapacity(Long.BYTES);
+ workBuf[0] = (byte) (i >> 56);
+ workBuf[1] = (byte) (i >> 48);
+ workBuf[2] = (byte) (i >> 40);
+ workBuf[3] = (byte) (i >> 32);
+ workBuf[4] = (byte) (i >> 24);
+ workBuf[5] = (byte) (i >> 16);
+ workBuf[6] = (byte) (i >> 8);
+ workBuf[7] = (byte) i;
+ putRawBytes(workBuf, 0, Long.BYTES);
+ }
+
+ public void putBoolean(boolean b) {
+ putByte(b ? (byte) 1 : (byte) 0);
+ }
+
+ /**
+ * Adds the bytes to the buffer and wipes the data from the
+ * input buffer <U>after</U> having added it - useful for sensitive
+ * information such as password
+ *
+ * @param b The buffer to add - OK if {@code null}
+ */
+ public void putAndWipeBytes(byte[] b) {
+ putAndWipeBytes(b, 0, NumberUtils.length(b));
+ }
+
+ public void putAndWipeBytes(byte[] b, int off, int len) {
+ putBytes(b, off, len);
+
+ for (int pos = off, index = 0; index < len; pos++, index++) {
+ b[pos] = (byte) 0;
+ }
+ }
+
+ public void putBytes(byte[] b) {
+ putBytes(b, 0, NumberUtils.length(b));
+ }
+
+ public void putBytes(byte[] b, int off, int len) {
+ putInt(len);
+ putRawBytes(b, off, len);
+ }
+
+ /**
+ * Encodes the {@link Objects#toString(Object, String) toString} value of each member.
+ *
+ * @param objects The objects to be encoded in the buffer - OK if
+ * {@code null}/empty
+ * @param prependLength If {@code true} then the list is preceded by
+ * a 32-bit count of the number of members in the list
+ * @see #putStringList(Collection, Charset, boolean)
+ */
+ public void putStringList(Collection<?> objects, boolean prependLength) {
+ putStringList(objects, StandardCharsets.UTF_8, prependLength);
+ }
+
+ /**
+ * Encodes the {@link Objects#toString(Object, String) toString} value of each member
+ *
+ * @param objects The objects to be encoded in the buffer - OK if
+ * {@code null}/empty
+ * @param charset The {@link Charset} to use for encoding
+ * @param prependLength If {@code true} then the list is preceded by
+ * a 32-bit count of the number of members in the list
+ * @see #putString(String, Charset)
+ */
+ public void putStringList(Collection<?> objects, Charset charset, boolean prependLength) {
+ int numObjects = GenericUtils.size(objects);
+ if (prependLength) {
+ putInt(numObjects);
+ }
+
+ if (numObjects <= 0) {
+ return;
+ }
+
+ objects.forEach(o -> putString(Objects.toString(o, null), charset));
+ }
+
+ public void putString(String string) {
+ putString(string, StandardCharsets.UTF_8);
+ }
+
+ public void putString(String string, Charset charset) {
+ if (GenericUtils.isEmpty(string)) {
+ putBytes(GenericUtils.EMPTY_BYTE_ARRAY);
+ } else {
+ putBytes(string.getBytes(charset));
+ }
+ }
+
+ /**
+ * Zeroes the input array <U>after</U> having put the characters in the
+ * buffer - useful for sensitive information such as passwords
+ *
+ * @param chars The characters to put in the buffer - may be {@code null}/empty
+ * @see #putAndWipeChars(char[], Charset)
+ * @see #putChars(char[], Charset)
+ */
+ public void putAndWipeChars(char[] chars) {
+ putAndWipeChars(chars, 0, GenericUtils.length(chars));
+ }
+
+ public void putAndWipeChars(char[] chars, int offset, int len) {
+ putAndWipeChars(chars, offset, len, StandardCharsets.UTF_8);
+ }
+
+ public void putAndWipeChars(char[] chars, Charset charset) {
+ putAndWipeChars(chars, 0, GenericUtils.length(chars), charset);
+ }
+
+ public void putAndWipeChars(char[] chars, int offset, int len, Charset charset) {
+ putChars(chars, offset, len, charset);
+ for (int pos = offset, index = 0; index < len; index++, pos++) {
+ chars[pos] = '\0';
+ }
+ }
+
+ public void putChars(char[] chars) {
+ putChars(chars, 0, GenericUtils.length(chars));
+ }
+
+ public void putChars(char[] chars, int offset, int len) {
+ putChars(chars, offset, len, StandardCharsets.UTF_8);
+ }
+
+ public void putChars(char[] chars, Charset charset) {
+ putChars(chars, 0, GenericUtils.length(chars), charset);
+ }
+
+ public void putChars(char[] chars, int offset, int len, Charset charset) {
+ if (len <= 0) {
+ putBytes(GenericUtils.EMPTY_BYTE_ARRAY);
+ } else {
+ putBuffer(charset.encode(CharBuffer.wrap(chars, offset, len)));
+ }
+ }
+
+ public void putMPInt(BigInteger bi) {
+ putMPInt(bi.toByteArray());
+ }
+
+ public void putMPInt(byte[] foo) {
+ if ((foo[0] & 0x80) != 0) {
+ putInt(foo.length + 1 /* padding */);
+ putByte((byte) 0);
+ } else {
+ putInt(foo.length);
+ }
+ putRawBytes(foo);
+ }
+
+ public void putRawBytes(byte[] d) {
+ putRawBytes(d, 0, d.length);
+ }
+
+ public abstract void putRawBytes(byte[] d, int off, int len);
+
+ public void putPublicKey(PublicKey key) {
+ int ow = wpos();
+ putInt(0);
+ int ow1 = wpos();
+ putRawPublicKey(key);
+ int ow2 = wpos();
+ wpos(ow);
+ putInt(ow2 - ow1);
+ wpos(ow2);
+ }
+
+ public void putRawPublicKey(PublicKey key) {
+ Objects.requireNonNull(key, "No key");
+ if (key instanceof RSAPublicKey) {
+ RSAPublicKey rsaPub = (RSAPublicKey) key;
+
+ putString(KeyPairProvider.SSH_RSA);
+ putMPInt(rsaPub.getPublicExponent());
+ putMPInt(rsaPub.getModulus());
+ } else if (key instanceof DSAPublicKey) {
+ DSAPublicKey dsaPub = (DSAPublicKey) key;
+ DSAParams dsaParams = dsaPub.getParams();
+
+ putString(KeyPairProvider.SSH_DSS);
+ putMPInt(dsaParams.getP());
+ putMPInt(dsaParams.getQ());
+ putMPInt(dsaParams.getG());
+ putMPInt(dsaPub.getY());
+ } else if (key instanceof ECPublicKey) {
+ ECPublicKey ecKey = (ECPublicKey) key;
+ ECParameterSpec ecParams = ecKey.getParams();
+ ECCurves curve = ECCurves.fromCurveParameters(ecParams);
+ if (curve == null) {
+ throw new BufferException("Unsupported EC curve parameters");
+ }
+
+ putString(curve.getKeyType());
+ putString(curve.getName());
+ putBytes(ECCurves.encodeECPoint(ecKey.getW(), ecParams));
+ } else if (SecurityUtils.EDDSA.equals(key.getAlgorithm())) {
+ SecurityUtils.putRawEDDSAPublicKey(this, key);
+ } else {
+ throw new BufferException("Unsupported raw public key algorithm: " + key.getAlgorithm());
+ }
+ }
+
+ public void putKeyPair(KeyPair kp) {
+ PublicKey pubKey = kp.getPublic();
+ PrivateKey prvKey = kp.getPrivate();
+ if (prvKey instanceof RSAPrivateCrtKey) {
+ RSAPublicKey rsaPub = (RSAPublicKey) pubKey;
+ RSAPrivateCrtKey rsaPrv = (RSAPrivateCrtKey) prvKey;
+
+ putString(KeyPairProvider.SSH_RSA);
+ putMPInt(rsaPub.getPublicExponent());
+ putMPInt(rsaPub.getModulus());
+ putMPInt(rsaPrv.getPrivateExponent());
+ putMPInt(rsaPrv.getCrtCoefficient());
+ putMPInt(rsaPrv.getPrimeQ());
+ putMPInt(rsaPrv.getPrimeP());
+ } else if (pubKey instanceof DSAPublicKey) {
+ DSAPublicKey dsaPub = (DSAPublicKey) pubKey;
+ DSAParams dsaParams = dsaPub.getParams();
+ DSAPrivateKey dsaPrv = (DSAPrivateKey) prvKey;
+
+ putString(KeyPairProvider.SSH_DSS);
+ putMPInt(dsaParams.getP());
+ putMPInt(dsaParams.getQ());
+ putMPInt(dsaParams.getG());
+ putMPInt(dsaPub.getY());
+ putMPInt(dsaPrv.getX());
+ } else if (pubKey instanceof ECPublicKey) {
+ ECPublicKey ecPub = (ECPublicKey) pubKey;
+ ECPrivateKey ecPriv = (ECPrivateKey) prvKey;
+ ECParameterSpec ecParams = ecPub.getParams();
+ ECCurves curve = ECCurves.fromCurveParameters(ecParams);
+ if (curve == null) {
+ throw new BufferException("Unsupported EC curve parameters");
+ }
+
+ putString(curve.getKeyType());
+ putString(curve.getName());
+ putBytes(ECCurves.encodeECPoint(ecPub.getW(), ecParams));
+ putMPInt(ecPriv.getS());
+ } else if (SecurityUtils.EDDSA.equals(pubKey.getAlgorithm())) {
+ SecurityUtils.putEDDSAKeyPair(this, pubKey, prvKey);
+ } else {
+ throw new BufferException("Unsupported key pair algorithm: " + pubKey.getAlgorithm());
+ }
+ }
+
+ protected void ensureCapacity(int capacity) {
+ ensureCapacity(capacity, BufferUtils.DEFAULT_BUFFER_GROWTH_FACTOR);
+ }
+
+ /**
+ * @param capacity The requires capacity
+ * @param growthFactor An {@link IntUnaryOperator} that is invoked
+ * if the current capacity is insufficient. The argument is the minimum
+ * required new data length, the function result should be the
+ * effective new data length to be allocated - if less than minimum
+ * then an exception is thrown
+ */
+ public abstract void ensureCapacity(int capacity, IntUnaryOperator growthFactor);
+
+ protected abstract int size();
+
+ @Override
+ public String toString() {
+ return "Buffer [rpos=" + rpos() + ", wpos=" + wpos() + ", size=" + size() + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferException.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferException.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferException.java
new file mode 100644
index 0000000..93f4e6b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/BufferException.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.buffer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class BufferException extends RuntimeException {
+ private static final long serialVersionUID = 658645233475011039L;
+
+ public BufferException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
[20/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
deleted file mode 100644
index bceca59..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHECDSAPrivateKeyEntryDecoder.java
+++ /dev/null
@@ -1,164 +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.loader.openssh;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchProviderException;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPrivateKeySpec;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Objects;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class OpenSSHECDSAPrivateKeyEntryDecoder extends AbstractPrivateKeyEntryDecoder<ECPublicKey, ECPrivateKey> {
- public static final OpenSSHECDSAPrivateKeyEntryDecoder INSTANCE = new OpenSSHECDSAPrivateKeyEntryDecoder();
-
- public OpenSSHECDSAPrivateKeyEntryDecoder() {
- super(ECPublicKey.class, ECPrivateKey.class, ECCurves.KEY_TYPES);
- }
-
- @Override
- public ECPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
- throws IOException, GeneralSecurityException {
- ECCurves curve = ECCurves.fromKeyType(keyType);
- if (curve == null) {
- throw new InvalidKeySpecException("Not an EC curve name: " + keyType);
- }
-
- if (!SecurityUtils.isECCSupported()) {
- throw new NoSuchProviderException("ECC not supported");
- }
-
- String keyCurveName = curve.getName();
- // see rfc5656 section 3.1
- String encCurveName = KeyEntryResolver.decodeString(keyData);
- if (!keyCurveName.equals(encCurveName)) {
- throw new InvalidKeySpecException("Mismatched key curve name (" + keyCurveName + ") vs. encoded one (" + encCurveName + ")");
- }
-
- byte[] pubKey = KeyEntryResolver.readRLEBytes(keyData);
- Objects.requireNonNull(pubKey, "No public point"); // TODO validate it is a valid ECPoint
- BigInteger s = KeyEntryResolver.decodeBigInt(keyData);
- ECParameterSpec params = curve.getParameters();
- return generatePrivateKey(new ECPrivateKeySpec(s, params));
- }
-
- @Override
- public String encodePrivateKey(OutputStream s, ECPrivateKey key) throws IOException {
- Objects.requireNonNull(key, "No private key provided");
- return null;
- }
-
- @Override
- public ECPublicKey recoverPublicKey(ECPrivateKey prvKey) throws GeneralSecurityException {
- ECCurves curve = ECCurves.fromECKey(prvKey);
- if (curve == null) {
- throw new InvalidKeyException("Unknown curve");
- }
- // TODO see how we can figure out the public value
- return super.recoverPublicKey(prvKey);
- }
-
- @Override
- public ECPublicKey clonePublicKey(ECPublicKey key) throws GeneralSecurityException {
- if (!SecurityUtils.isECCSupported()) {
- throw new NoSuchProviderException("ECC not supported");
- }
-
- if (key == null) {
- return null;
- }
-
- ECParameterSpec params = key.getParams();
- if (params == null) {
- throw new InvalidKeyException("Missing parameters in key");
- }
-
- return generatePublicKey(new ECPublicKeySpec(key.getW(), params));
- }
-
- @Override
- public ECPrivateKey clonePrivateKey(ECPrivateKey key) throws GeneralSecurityException {
- if (!SecurityUtils.isECCSupported()) {
- throw new NoSuchProviderException("ECC not supported");
- }
-
- if (key == null) {
- return null;
- }
-
- ECParameterSpec params = key.getParams();
- if (params == null) {
- throw new InvalidKeyException("Missing parameters in key");
- }
-
- return generatePrivateKey(new ECPrivateKeySpec(key.getS(), params));
- }
-
- @Override
- public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
- if (SecurityUtils.isECCSupported()) {
- return SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
- } else {
- throw new NoSuchProviderException("ECC not supported");
- }
- }
-
- @Override
- public KeyPair generateKeyPair(int keySize) throws GeneralSecurityException {
- ECCurves curve = ECCurves.fromCurveSize(keySize);
- if (curve == null) {
- throw new InvalidKeySpecException("Unknown curve for key size=" + keySize);
- }
-
- KeyPairGenerator gen = getKeyPairGenerator();
- gen.initialize(curve.getParameters());
- return gen.generateKeyPair();
- }
-
- @Override
- public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
- if (SecurityUtils.isECCSupported()) {
- return SecurityUtils.getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
- } else {
- throw new NoSuchProviderException("ECC not supported");
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
deleted file mode 100644
index 95db256..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
+++ /dev/null
@@ -1,358 +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.loader.openssh;
-
-import java.io.ByteArrayInputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.TreeMap;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
-import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Basic support for <A HREF="http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?rev=1.1&content-type=text/x-cvsweb-markup">OpenSSH key file(s)</A>
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser {
- public static final String BEGIN_MARKER = "BEGIN OPENSSH PRIVATE KEY";
- public static final List<String> BEGINNERS =
- Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
-
- public static final String END_MARKER = "END OPENSSH PRIVATE KEY";
- public static final List<String> ENDERS =
- Collections.unmodifiableList(Collections.singletonList(END_MARKER));
-
- public static final String AUTH_MAGIC = "openssh-key-v1";
- public static final OpenSSHKeyPairResourceParser INSTANCE = new OpenSSHKeyPairResourceParser();
-
- private static final byte[] AUTH_MAGIC_BYTES = AUTH_MAGIC.getBytes(StandardCharsets.UTF_8);
- private static final Map<String, PrivateKeyEntryDecoder<?, ?>> BY_KEY_TYPE_DECODERS_MAP =
- new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
- private static final Map<Class<?>, PrivateKeyEntryDecoder<?, ?>> BY_KEY_CLASS_DECODERS_MAP =
- new HashMap<>();
-
- static {
- registerPrivateKeyEntryDecoder(OpenSSHRSAPrivateKeyDecoder.INSTANCE);
- registerPrivateKeyEntryDecoder(OpenSSHDSSPrivateKeyEntryDecoder.INSTANCE);
-
- if (SecurityUtils.isECCSupported()) {
- registerPrivateKeyEntryDecoder(OpenSSHECDSAPrivateKeyEntryDecoder.INSTANCE);
- }
- if (SecurityUtils.isEDDSACurveSupported()) {
- registerPrivateKeyEntryDecoder(SecurityUtils.getOpenSSHEDDSAPrivateKeyEntryDecoder());
- }
- }
-
- public OpenSSHKeyPairResourceParser() {
- super(BEGINNERS, ENDERS);
- }
-
- @Override
- public Collection<KeyPair> extractKeyPairs(
- String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
- stream = validateStreamMagicMarker(resourceKey, stream);
-
- String cipher = KeyEntryResolver.decodeString(stream);
- if (!OpenSSHParserContext.IS_NONE_CIPHER.test(cipher)) {
- throw new NoSuchAlgorithmException("Unsupported cipher: " + cipher);
- }
-
- boolean debugEnabled = log.isDebugEnabled();
- if (debugEnabled) {
- log.debug("extractKeyPairs({}) cipher={}", resourceKey, cipher);
- }
-
- String kdfName = KeyEntryResolver.decodeString(stream);
- if (!OpenSSHParserContext.IS_NONE_KDF.test(kdfName)) {
- throw new NoSuchAlgorithmException("Unsupported KDF: " + kdfName);
- }
-
- byte[] kdfOptions = KeyEntryResolver.readRLEBytes(stream);
- if (debugEnabled) {
- log.debug("extractKeyPairs({}) KDF={}, options={}",
- resourceKey, kdfName, BufferUtils.toHex(':', kdfOptions));
- }
-
- int numKeys = KeyEntryResolver.decodeInt(stream);
- if (numKeys <= 0) {
- if (debugEnabled) {
- log.debug("extractKeyPairs({}) no encoded keys", resourceKey);
- }
- return Collections.emptyList();
- }
-
- List<PublicKey> publicKeys = new ArrayList<>(numKeys);
- OpenSSHParserContext context = new OpenSSHParserContext(cipher, kdfName, kdfOptions);
- boolean traceEnabled = log.isTraceEnabled();
- for (int index = 1; index <= numKeys; index++) {
- PublicKey pubKey = readPublicKey(resourceKey, context, stream);
- ValidateUtils.checkNotNull(pubKey, "Empty public key #%d in %s", index, resourceKey);
- if (traceEnabled) {
- log.trace("extractKeyPairs({}) read public key #{}: {} {}",
- resourceKey, index, KeyUtils.getKeyType(pubKey), KeyUtils.getFingerPrint(pubKey));
- }
- publicKeys.add(pubKey);
- }
-
- byte[] privateData = KeyEntryResolver.readRLEBytes(stream);
- try (InputStream bais = new ByteArrayInputStream(privateData)) {
- return readPrivateKeys(resourceKey, context, publicKeys, passwordProvider, bais);
- }
- }
-
- protected PublicKey readPublicKey(
- String resourceKey, OpenSSHParserContext context, InputStream stream)
- throws IOException, GeneralSecurityException {
- byte[] keyData = KeyEntryResolver.readRLEBytes(stream);
- try (InputStream bais = new ByteArrayInputStream(keyData)) {
- String keyType = KeyEntryResolver.decodeString(bais);
- PublicKeyEntryDecoder<?, ?> decoder = KeyUtils.getPublicKeyEntryDecoder(keyType);
- if (decoder == null) {
- throw new NoSuchAlgorithmException("Unsupported key type (" + keyType + ") in " + resourceKey);
- }
-
- return decoder.decodePublicKey(keyType, bais);
- }
- }
-
- protected List<KeyPair> readPrivateKeys(
- String resourceKey, OpenSSHParserContext context, Collection<? extends PublicKey> publicKeys,
- FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
- if (GenericUtils.isEmpty(publicKeys)) {
- return Collections.emptyList();
- }
-
- boolean traceEnabled = log.isTraceEnabled();
- int check1 = KeyEntryResolver.decodeInt(stream);
- int check2 = KeyEntryResolver.decodeInt(stream);
- if (traceEnabled) {
- log.trace("readPrivateKeys({}) check1=0x{}, check2=0x{}",
- resourceKey, Integer.toHexString(check1), Integer.toHexString(check2));
- }
-
- List<KeyPair> keyPairs = new ArrayList<>(publicKeys.size());
- for (PublicKey pubKey : publicKeys) {
- String pubType = KeyUtils.getKeyType(pubKey);
- int keyIndex = keyPairs.size() + 1;
- if (traceEnabled) {
- log.trace("extractKeyPairs({}) read private key #{}: {}",
- resourceKey, keyIndex, pubType);
- }
-
- Map.Entry<PrivateKey, String> prvData = readPrivateKey(resourceKey, context, pubType, passwordProvider, stream);
- PrivateKey prvKey = (prvData == null) ? null : prvData.getKey();
- ValidateUtils.checkNotNull(prvKey, "Empty private key #%d in %s", keyIndex, resourceKey);
-
- String prvType = KeyUtils.getKeyType(prvKey);
- ValidateUtils.checkTrue(Objects.equals(pubType, prvType),
- "Mismatched public (%s) vs. private (%s) key type #%d in %s",
- pubType, prvType, keyIndex, resourceKey);
-
- if (traceEnabled) {
- log.trace("extractKeyPairs({}) add private key #{}: {} {}",
- resourceKey, keyIndex, prvType, prvData.getValue());
- }
- keyPairs.add(new KeyPair(pubKey, prvKey));
- }
-
- return keyPairs;
- }
-
- protected SimpleImmutableEntry<PrivateKey, String> readPrivateKey(
- String resourceKey, OpenSSHParserContext context, String keyType, FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
- String prvType = KeyEntryResolver.decodeString(stream);
- if (!Objects.equals(keyType, prvType)) {
- throw new StreamCorruptedException("Mismatched private key type: "
- + ", expected=" + keyType + ", actual=" + prvType
- + " in " + resourceKey);
- }
-
- PrivateKeyEntryDecoder<?, ?> decoder = getPrivateKeyEntryDecoder(prvType);
- if (decoder == null) {
- throw new NoSuchAlgorithmException("Unsupported key type (" + prvType + ") in " + resourceKey);
- }
-
- PrivateKey prvKey = decoder.decodePrivateKey(prvType, passwordProvider, stream);
- if (prvKey == null) {
- throw new InvalidKeyException("Cannot parse key type (" + prvType + ") in " + resourceKey);
- }
-
- String comment = KeyEntryResolver.decodeString(stream);
- return new SimpleImmutableEntry<>(prvKey, comment);
- }
-
- protected <S extends InputStream> S validateStreamMagicMarker(String resourceKey, S stream) throws IOException {
- byte[] actual = new byte[AUTH_MAGIC_BYTES.length];
- IoUtils.readFully(stream, actual);
- if (!Arrays.equals(AUTH_MAGIC_BYTES, actual)) {
- throw new StreamCorruptedException(resourceKey + ": Mismatched magic marker value: " + BufferUtils.toHex(':', actual));
- }
-
- int eos = stream.read();
- if (eos == -1) {
- throw new EOFException(resourceKey + ": Premature EOF after magic marker value");
- }
-
- if (eos != 0) {
- throw new StreamCorruptedException(resourceKey + ": Missing EOS after magic marker value: 0x" + Integer.toHexString(eos));
- }
-
- return stream;
- }
-
- /**
- * @param decoder The decoder to register
- * @throws IllegalArgumentException if no decoder or not key type or no
- * supported names for the decoder
- * @see PrivateKeyEntryDecoder#getPublicKeyType()
- * @see PrivateKeyEntryDecoder#getSupportedTypeNames()
- */
- public static void registerPrivateKeyEntryDecoder(PrivateKeyEntryDecoder<?, ?> decoder) {
- Objects.requireNonNull(decoder, "No decoder specified");
-
- Class<?> pubType = Objects.requireNonNull(decoder.getPublicKeyType(), "No public key type declared");
- Class<?> prvType = Objects.requireNonNull(decoder.getPrivateKeyType(), "No private key type declared");
- synchronized (BY_KEY_CLASS_DECODERS_MAP) {
- BY_KEY_CLASS_DECODERS_MAP.put(pubType, decoder);
- BY_KEY_CLASS_DECODERS_MAP.put(prvType, decoder);
- }
-
- Collection<String> names = ValidateUtils.checkNotNullAndNotEmpty(decoder.getSupportedTypeNames(), "No supported key type");
- synchronized (BY_KEY_TYPE_DECODERS_MAP) {
- for (String n : names) {
- PrivateKeyEntryDecoder<?, ?> prev = BY_KEY_TYPE_DECODERS_MAP.put(n, decoder);
- if (prev != null) {
- //noinspection UnnecessaryContinue
- continue; // debug breakpoint
- }
- }
- }
- }
-
- /**
- * @param keyType The {@code OpenSSH} key type string - e.g., {@code ssh-rsa, ssh-dss}
- * - ignored if {@code null}/empty
- * @return The registered {@link PrivateKeyEntryDecoder} or {code null} if not found
- */
- public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(String keyType) {
- if (GenericUtils.isEmpty(keyType)) {
- return null;
- }
-
- synchronized (BY_KEY_TYPE_DECODERS_MAP) {
- return BY_KEY_TYPE_DECODERS_MAP.get(keyType);
- }
- }
-
- /**
- * @param kp The {@link KeyPair} to examine - ignored if {@code null}
- * @return The matching {@link PrivateKeyEntryDecoder} provided <U>both</U>
- * the public and private keys have the same decoder - {@code null} if no
- * match found
- * @see #getPrivateKeyEntryDecoder(Key)
- */
- public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(KeyPair kp) {
- if (kp == null) {
- return null;
- }
-
- PrivateKeyEntryDecoder<?, ?> d1 = getPrivateKeyEntryDecoder(kp.getPublic());
- PrivateKeyEntryDecoder<?, ?> d2 = getPrivateKeyEntryDecoder(kp.getPrivate());
- if (d1 == d2) {
- return d1;
- } else {
- return null; // some kind of mixed keys...
- }
- }
-
- /**
- * @param key The {@link Key} (public or private) - ignored if {@code null}
- * @return The registered {@link PrivateKeyEntryDecoder} for this key or {code null} if no match found
- * @see #getPrivateKeyEntryDecoder(Class)
- */
- public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(Key key) {
- if (key == null) {
- return null;
- } else {
- return getPrivateKeyEntryDecoder(key.getClass());
- }
- }
-
- /**
- * @param keyType The key {@link Class} - ignored if {@code null} or not a {@link Key}
- * compatible type
- * @return The registered {@link PrivateKeyEntryDecoder} or {code null} if no match found
- */
- public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(Class<?> keyType) {
- if ((keyType == null) || (!Key.class.isAssignableFrom(keyType))) {
- return null;
- }
-
- synchronized (BY_KEY_TYPE_DECODERS_MAP) {
- PrivateKeyEntryDecoder<?, ?> decoder = BY_KEY_CLASS_DECODERS_MAP.get(keyType);
- if (decoder != null) {
- return decoder;
- }
-
- // in case it is a derived class
- for (PrivateKeyEntryDecoder<?, ?> dec : BY_KEY_CLASS_DECODERS_MAP.values()) {
- Class<?> pubType = dec.getPublicKeyType();
- Class<?> prvType = dec.getPrivateKeyType();
- if (pubType.isAssignableFrom(keyType) || prvType.isAssignableFrom(keyType)) {
- return dec;
- }
- }
- }
-
- return null;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHParserContext.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHParserContext.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHParserContext.java
deleted file mode 100644
index 07f2a9a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHParserContext.java
+++ /dev/null
@@ -1,83 +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.loader.openssh;
-
-import java.util.function.Predicate;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class OpenSSHParserContext {
- public static final String NONE_CIPHER = "none";
- public static final Predicate<String> IS_NONE_CIPHER = c -> GenericUtils.isEmpty(c) || NONE_CIPHER.equalsIgnoreCase(c);
-
- public static final String NONE_KDF = "none";
- public static final Predicate<String> IS_NONE_KDF = c -> GenericUtils.isEmpty(c) || NONE_KDF.equalsIgnoreCase(c);
-
- private String cipherName;
- private String kdfName;
- private byte[] kdfOptions;
-
- public OpenSSHParserContext() {
- super();
- }
-
- public OpenSSHParserContext(String cipherName, String kdfName, byte... kdfOptions) {
- this.cipherName = cipherName;
- this.kdfName = kdfName;
- this.kdfOptions = kdfOptions;
- }
-
- public String getCipherName() {
- return cipherName;
- }
-
- public void setCipherName(String cipherName) {
- this.cipherName = cipherName;
- }
-
- public String getKdfName() {
- return kdfName;
- }
-
- public void setKdfName(String kdfName) {
- this.kdfName = kdfName;
- }
-
- public byte[] getKdfOptions() {
- return kdfOptions;
- }
-
- public void setKdfOptions(byte[] kdfOptions) {
- this.kdfOptions = kdfOptions;
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName()
- + "[cipher=" + getCipherName()
- + ", kdfName=" + getKdfName()
- + ", kdfOptions=" + BufferUtils.toHex(':', getKdfOptions())
- + "]";
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
deleted file mode 100644
index 72e003f..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHRSAPrivateKeyDecoder.java
+++ /dev/null
@@ -1,135 +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.loader.openssh;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.KeyPairGenerator;
-import java.security.interfaces.RSAPrivateCrtKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.RSAPrivateCrtKeySpec;
-import java.security.spec.RSAPrivateKeySpec;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.Collections;
-import java.util.Objects;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyEntryResolver;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class OpenSSHRSAPrivateKeyDecoder extends AbstractPrivateKeyEntryDecoder<RSAPublicKey, RSAPrivateKey> {
- public static final BigInteger DEFAULT_PUBLIC_EXPONENT = new BigInteger("65537");
- public static final OpenSSHRSAPrivateKeyDecoder INSTANCE = new OpenSSHRSAPrivateKeyDecoder();
-
- public OpenSSHRSAPrivateKeyDecoder() {
- super(RSAPublicKey.class, RSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_RSA)));
- }
-
- @Override
- public RSAPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
- throws IOException, GeneralSecurityException {
- if (!KeyPairProvider.SSH_RSA.equals(keyType)) { // just in case we were invoked directly
- throw new InvalidKeySpecException("Unexpected key type: " + keyType);
- }
-
- BigInteger n = KeyEntryResolver.decodeBigInt(keyData);
- BigInteger e = KeyEntryResolver.decodeBigInt(keyData);
- if (!Objects.equals(e, DEFAULT_PUBLIC_EXPONENT)) {
- log.warn("decodePrivateKey({}) non-standard RSA exponent found: {}", keyType, e);
- }
-
- BigInteger d = KeyEntryResolver.decodeBigInt(keyData);
- BigInteger inverseQmodP = KeyEntryResolver.decodeBigInt(keyData);
- Objects.requireNonNull(inverseQmodP, "Missing iqmodp"); // TODO run some validation on it
- BigInteger p = KeyEntryResolver.decodeBigInt(keyData);
- BigInteger q = KeyEntryResolver.decodeBigInt(keyData);
- BigInteger modulus = p.multiply(q);
- if (!Objects.equals(n, modulus)) {
- log.warn("decodePrivateKey({}) mismatched modulus values: encoded={}, calculated={}",
- keyType, n, modulus);
- }
-
- return generatePrivateKey(new RSAPrivateKeySpec(n, d));
- }
-
- @Override
- public boolean isPublicKeyRecoverySupported() {
- return true;
- }
-
- @Override
- public RSAPublicKey recoverPublicKey(RSAPrivateKey privateKey) throws GeneralSecurityException {
- return KeyUtils.recoverRSAPublicKey(privateKey);
- }
-
- @Override
- public RSAPublicKey clonePublicKey(RSAPublicKey key) throws GeneralSecurityException {
- if (key == null) {
- return null;
- } else {
- return generatePublicKey(new RSAPublicKeySpec(key.getModulus(), key.getPublicExponent()));
- }
- }
-
- @Override
- public RSAPrivateKey clonePrivateKey(RSAPrivateKey key) throws GeneralSecurityException {
- if (key == null) {
- return null;
- }
-
- if (!(key instanceof RSAPrivateCrtKey)) {
- throw new InvalidKeyException("Cannot clone a non-RSAPrivateCrtKey: " + key.getClass().getSimpleName());
- }
-
- RSAPrivateCrtKey rsaPrv = (RSAPrivateCrtKey) key;
- return generatePrivateKey(
- new RSAPrivateCrtKeySpec(
- rsaPrv.getModulus(),
- rsaPrv.getPublicExponent(),
- rsaPrv.getPrivateExponent(),
- rsaPrv.getPrimeP(),
- rsaPrv.getPrimeQ(),
- rsaPrv.getPrimeExponentP(),
- rsaPrv.getPrimeExponentQ(),
- rsaPrv.getCrtCoefficient()));
- }
-
- @Override
- public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
- return SecurityUtils.getKeyPairGenerator(KeyUtils.RSA_ALGORITHM);
- }
-
- @Override
- public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
- return SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
deleted file mode 100644
index bee13d6..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
+++ /dev/null
@@ -1,167 +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.loader.pem;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import javax.security.auth.login.CredentialException;
-import javax.security.auth.login.FailedLoginException;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
-import org.apache.sshd.common.config.keys.loader.PrivateKeyEncryptionContext;
-import org.apache.sshd.common.config.keys.loader.PrivateKeyObfuscator;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.BufferUtils;
-
-/**
- * Base class for PEM file key-pair loaders
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class AbstractPEMResourceKeyPairParser
- extends AbstractKeyPairResourceParser
- implements KeyPairPEMResourceParser {
- private final String algo;
- private final String algId;
-
- protected AbstractPEMResourceKeyPairParser(String algo, String algId, List<String> beginners, List<String> enders) {
- super(beginners, enders);
- this.algo = ValidateUtils.checkNotNullAndNotEmpty(algo, "No encryption algorithm provided");
- this.algId = ValidateUtils.checkNotNullAndNotEmpty(algId, "No algorithm identifier provided");
- }
-
- @Override
- public String getAlgorithm() {
- return algo;
- }
-
- @Override
- public String getAlgorithmIdentifier() {
- return algId;
- }
-
- @Override
- public Collection<KeyPair> extractKeyPairs(
- String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
- throws IOException, GeneralSecurityException {
- if (GenericUtils.isEmpty(lines)) {
- return Collections.emptyList();
- }
-
- Boolean encrypted = null;
- byte[] initVector = null;
- String algInfo = null;
- int dataStartIndex = -1;
- for (int index = 0; index < lines.size(); index++) {
- String line = GenericUtils.trimToEmpty(lines.get(index));
- if (GenericUtils.isEmpty(line)) {
- continue;
- }
-
- // check if header line - if not, assume data lines follow
- int headerPos = line.indexOf(':');
- if (headerPos < 0) {
- dataStartIndex = index;
- break;
- }
-
- if (line.startsWith("Proc-Type:")) {
- if (encrypted != null) {
- throw new StreamCorruptedException("Multiple encryption indicators in " + resourceKey);
- }
-
- line = line.substring(headerPos + 1).trim();
- line = line.toUpperCase();
- encrypted = Boolean.valueOf(line.contains("ENCRYPTED"));
- } else if (line.startsWith("DEK-Info:")) {
- if ((initVector != null) || (algInfo != null)) {
- throw new StreamCorruptedException("Multiple encryption settings in " + resourceKey);
- }
-
- line = line.substring(headerPos + 1).trim();
- headerPos = line.indexOf(',');
- if (headerPos < 0) {
- throw new StreamCorruptedException(resourceKey + ": Missing encryption data values separator in line '" + line + "'");
- }
-
- algInfo = line.substring(0, headerPos).trim();
-
- String algInitVector = line.substring(headerPos + 1).trim();
- initVector = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, algInitVector);
- }
- }
-
- if (dataStartIndex < 0) {
- throw new StreamCorruptedException("No data lines (only headers or empty) found in " + resourceKey);
- }
-
- List<String> dataLines = lines.subList(dataStartIndex, lines.size());
- if ((encrypted != null) || (algInfo != null) || (initVector != null)) {
- if (passwordProvider == null) {
- throw new CredentialException("Missing password provider for encrypted resource=" + resourceKey);
- }
-
- String password = passwordProvider.getPassword(resourceKey);
- if (GenericUtils.isEmpty(password)) {
- throw new FailedLoginException("No password data for encrypted resource=" + resourceKey);
- }
-
- PrivateKeyEncryptionContext encContext = new PrivateKeyEncryptionContext(algInfo);
- encContext.setPassword(password);
- encContext.setInitVector(initVector);
- byte[] encryptedData = KeyPairResourceParser.extractDataBytes(dataLines);
- byte[] decodedData = applyPrivateKeyCipher(encryptedData, encContext, false);
- try (InputStream bais = new ByteArrayInputStream(decodedData)) {
- return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, bais);
- }
- }
-
- return super.extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, dataLines);
- }
-
- protected byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException {
- String cipherName = encContext.getCipherName();
- PrivateKeyObfuscator o = encContext.resolvePrivateKeyObfuscator();
- if (o == null) {
- throw new NoSuchAlgorithmException("decryptPrivateKeyData(" + encContext + ")[encrypt=" + encryptIt + "] unknown cipher: " + cipherName);
- }
-
- if (encryptIt) {
- byte[] initVector = encContext.getInitVector();
- if (GenericUtils.isEmpty(initVector)) {
- initVector = o.generateInitializationVector(encContext);
- encContext.setInitVector(initVector);
- }
- }
-
- return o.applyPrivateKeyCipher(bytes, encContext, encryptIt);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
deleted file mode 100644
index 0c357af..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
+++ /dev/null
@@ -1,126 +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.loader.pem;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.DSAPrivateKeySpec;
-import java.security.spec.DSAPublicKeySpec;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.der.ASN1Object;
-import org.apache.sshd.common.util.io.der.ASN1Type;
-import org.apache.sshd.common.util.io.der.DERParser;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DSSPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParser {
- // Not exactly according to standard but good enough
- public static final String BEGIN_MARKER = "BEGIN DSA PRIVATE KEY";
- public static final List<String> BEGINNERS =
- Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
-
- public static final String END_MARKER = "END DSA PRIVATE KEY";
- public static final List<String> ENDERS =
- Collections.unmodifiableList(Collections.singletonList(END_MARKER));
-
- /**
- * @see <A HREF="https://tools.ietf.org/html/rfc3279#section-2.3.2">RFC-3279 section 2.3.2</A>
- */
- public static final String DSS_OID = "1.2.840.10040.4.1";
-
- public static final DSSPEMResourceKeyPairParser INSTANCE = new DSSPEMResourceKeyPairParser();
-
- public DSSPEMResourceKeyPairParser() {
- super(KeyUtils.DSS_ALGORITHM, DSS_OID, BEGINNERS, ENDERS);
- }
-
- @Override
- public Collection<KeyPair> extractKeyPairs(
- String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
- KeyPair kp = decodeDSSKeyPair(SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM), stream, false);
- return Collections.singletonList(kp);
- }
-
- /**
- * <p>The ASN.1 syntax for the private key:</P>
- * <pre><code>
- * DSAPrivateKey ::= SEQUENCE {
- * version Version,
- * p INTEGER,
- * q INTEGER,
- * g INTEGER,
- * y INTEGER,
- * x INTEGER
- * }
- * </code></pre>
- * @param kf The {@link KeyFactory} To use to generate the keys
- * @param s The {@link InputStream} containing the encoded bytes
- * @param okToClose <code>true</code> if the method may close the input
- * stream regardless of success or failure
- * @return The recovered {@link KeyPair}
- * @throws IOException If failed to read or decode the bytes
- * @throws GeneralSecurityException If failed to generate the keys
- */
- public static KeyPair decodeDSSKeyPair(KeyFactory kf, InputStream s, boolean okToClose)
- throws IOException, GeneralSecurityException {
- ASN1Object sequence;
- try (DERParser parser = new DERParser(NoCloseInputStream.resolveInputStream(s, okToClose))) {
- sequence = parser.readObject();
- }
-
- if (!ASN1Type.SEQUENCE.equals(sequence.getObjType())) {
- throw new IOException("Invalid DER: not a sequence: " + sequence.getObjType());
- }
-
- // Parse inside the sequence
- try (DERParser parser = sequence.createParser()) {
- // Skip version
- ASN1Object version = parser.readObject();
- if (version == null) {
- throw new StreamCorruptedException("No version");
- }
-
- BigInteger p = parser.readObject().asInteger();
- BigInteger q = parser.readObject().asInteger();
- BigInteger g = parser.readObject().asInteger();
- BigInteger y = parser.readObject().asInteger();
- BigInteger x = parser.readObject().asInteger();
- PublicKey pubKey = kf.generatePublic(new DSAPublicKeySpec(y, p, q, g));
- PrivateKey prvKey = kf.generatePrivate(new DSAPrivateKeySpec(x, p, q, g));
- return new KeyPair(pubKey, prvKey);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
deleted file mode 100644
index dd8d2ea..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
+++ /dev/null
@@ -1,220 +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.loader.pem;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.NoSuchProviderException;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPrivateKeySpec;
-import java.security.spec.ECPublicKeySpec;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.der.ASN1Object;
-import org.apache.sshd.common.util.io.der.ASN1Type;
-import org.apache.sshd.common.util.io.der.DERParser;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParser {
- public static final String BEGIN_MARKER = "BEGIN EC PRIVATE KEY";
- public static final List<String> BEGINNERS =
- Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
-
- public static final String END_MARKER = "END EC PRIVATE KEY";
- public static final List<String> ENDERS =
- Collections.unmodifiableList(Collections.singletonList(END_MARKER));
-
- /**
- * @see <A HREF="https://tools.ietf.org/html/rfc3279#section-2.3.5">RFC-3279 section 2.3.5</A>
- */
- public static final String ECDSA_OID = "1.2.840.10045.2.1";
-
- public static final ECDSAPEMResourceKeyPairParser INSTANCE = new ECDSAPEMResourceKeyPairParser();
-
- public ECDSAPEMResourceKeyPairParser() {
- super(KeyUtils.EC_ALGORITHM, ECDSA_OID, BEGINNERS, ENDERS);
- }
-
- @Override
- public Collection<KeyPair> extractKeyPairs(
- String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
- Map.Entry<ECPublicKeySpec, ECPrivateKeySpec> spec = decodeECPrivateKeySpec(stream, false);
- if (!SecurityUtils.isECCSupported()) {
- throw new NoSuchProviderException("ECC not supported");
- }
-
- KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
- ECPublicKey pubKey = (ECPublicKey) kf.generatePublic(spec.getKey());
- ECPrivateKey prvKey = (ECPrivateKey) kf.generatePrivate(spec.getValue());
- KeyPair kp = new KeyPair(pubKey, prvKey);
- return Collections.singletonList(kp);
- }
-
- /**
- * <P>ASN.1 syntax according to rfc5915 is:</P></BR>
- * <PRE><CODE>
- * ECPrivateKey ::= SEQUENCE {
- * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
- * privateKey OCTET STRING,
- * parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
- * publicKey [1] BIT STRING OPTIONAL
- * }
- * </CODE></PRE>
- * <P><I>ECParameters</I> syntax according to RFC5480:</P></BR>
- * <PRE><CODE>
- * ECParameters ::= CHOICE {
- * namedCurve OBJECT IDENTIFIER
- * -- implicitCurve NULL
- * -- specifiedCurve SpecifiedECDomain
- * }
- * </CODE></PRE>
- * @param inputStream The {@link InputStream} containing the DER encoded data
- * @param okToClose {@code true} if OK to close the DER stream once parsing complete
- * @return The decoded {@link SimpleImmutableEntry} of {@link ECPublicKeySpec} and {@link ECPrivateKeySpec}
- * @throws IOException If failed to to decode the DER stream
- */
- public static SimpleImmutableEntry<ECPublicKeySpec, ECPrivateKeySpec> decodeECPrivateKeySpec(InputStream inputStream, boolean okToClose) throws IOException {
- ASN1Object sequence;
- try (DERParser parser = new DERParser(NoCloseInputStream.resolveInputStream(inputStream, okToClose))) {
- sequence = parser.readObject();
- }
-
- if (!ASN1Type.SEQUENCE.equals(sequence.getObjType())) {
- throw new IOException("Invalid DER: not a sequence: " + sequence.getObjType());
- }
-
- // Parse inside the sequence
- try (DERParser parser = sequence.createParser()) {
- ECPrivateKeySpec prvSpec = decodeECPrivateKeySpec(parser);
- ECCurves curve = ECCurves.fromCurveParameters(prvSpec.getParams());
- if (curve == null) {
- throw new StreamCorruptedException("Unknown curve");
- }
-
- ECPoint w = decodeECPublicKeyValue(curve, parser);
- ECPublicKeySpec pubSpec = new ECPublicKeySpec(w, prvSpec.getParams());
- return new SimpleImmutableEntry<>(pubSpec, prvSpec);
- }
- }
-
- public static final ECPrivateKeySpec decodeECPrivateKeySpec(DERParser parser) throws IOException {
- // see openssl asn1parse -inform PEM -in ...file... -dump
- ASN1Object versionObject = parser.readObject(); // Skip version
- if (versionObject == null) {
- throw new StreamCorruptedException("No version");
- }
-
- // as per RFC-5915 section 3
- BigInteger version = versionObject.asInteger();
- if (!BigInteger.ONE.equals(version)) {
- throw new StreamCorruptedException("Bad version value: " + version);
- }
-
- ASN1Object keyObject = parser.readObject();
- if (keyObject == null) {
- throw new StreamCorruptedException("No private key value");
- }
-
- ASN1Type objType = keyObject.getObjType();
- if (!ASN1Type.OCTET_STRING.equals(objType)) {
- throw new StreamCorruptedException("Non-matching private key object type: " + objType);
- }
-
- ASN1Object paramsObject = parser.readObject();
- if (paramsObject == null) {
- throw new StreamCorruptedException("No parameters value");
- }
-
- // TODO make sure params object tag is 0xA0
-
- final List<Integer> curveOID;
- try (DERParser paramsParser = paramsObject.createParser()) {
- ASN1Object namedCurve = paramsParser.readObject();
- if (namedCurve == null) {
- throw new StreamCorruptedException("Missing named curve parameter");
- }
-
- curveOID = namedCurve.asOID();
- }
-
- ECCurves curve = ECCurves.fromOIDValue(curveOID);
- if (curve == null) {
- throw new StreamCorruptedException("Unknown curve OID: " + curveOID);
- }
-
- BigInteger s = ECCurves.octetStringToInteger(keyObject.getPureValueBytes());
- return new ECPrivateKeySpec(s, curve.getParameters());
- }
-
- /**
- * <P>ASN.1 syntax according to rfc5915 is:</P></BR>
- * <PRE>
- * publicKey [1] BIT STRING OPTIONAL
- * </PRE>
- * @param curve The {@link ECCurves} curve
- * @param parser The {@link DERParser} assumed to be positioned at the
- * start of the data
- * @return The encoded {@link ECPoint}
- * @throws IOException If failed to create the point
- */
- public static final ECPoint decodeECPublicKeyValue(ECCurves curve, DERParser parser) throws IOException {
- // see openssl asn1parse -inform PEM -in ...file... -dump
- ASN1Object dataObject = parser.readObject();
- if (dataObject == null) {
- throw new StreamCorruptedException("No public key data bytes");
- }
-
- try (DERParser dataParser = dataObject.createParser()) {
- ASN1Object pointData = dataParser.readObject();
- if (pointData == null) {
- throw new StreamCorruptedException("Missing public key data parameter");
- }
-
- ASN1Type objType = pointData.getObjType();
- if (!ASN1Type.BIT_STRING.equals(objType)) {
- throw new StreamCorruptedException("Non-matching public key object type: " + objType);
- }
-
- // see https://tools.ietf.org/html/rfc5480#section-2.2
- byte[] octets = pointData.getValue();
- return ECCurves.octetStringToEcPoint(octets);
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/KeyPairPEMResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/KeyPairPEMResourceParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/KeyPairPEMResourceParser.java
deleted file mode 100644
index 07c7e44..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/KeyPairPEMResourceParser.java
+++ /dev/null
@@ -1,37 +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.loader.pem;
-
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface KeyPairPEMResourceParser extends KeyPairResourceParser {
- /**
- * @return The encryption algorithm name - e.g., "RSA", "DSA"
- */
- String getAlgorithm();
-
- /**
- * @return The OID used to identify this algorithm in DER encodings - e.g., RSA=1.2.840.113549.1.1.1
- */
- String getAlgorithmIdentifier();
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java
deleted file mode 100644
index b6749da..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PEMResourceParserUtils.java
+++ /dev/null
@@ -1,109 +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.loader.pem;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.TreeMap;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
-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 PEMResourceParserUtils {
- public static final KeyPairResourceParser PROXY = new KeyPairResourceParser() {
- @Override
- public Collection<KeyPair> loadKeyPairs(
- String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
- throws IOException, GeneralSecurityException {
- @SuppressWarnings("synthetic-access")
- KeyPairResourceParser proxy = PROXY_HOLDER.get();
- return (proxy == null) ? Collections.<KeyPair>emptyList() : proxy.loadKeyPairs(resourceKey, passwordProvider, lines);
- }
-
- @Override
- public boolean canExtractKeyPairs(String resourceKey, List<String> lines)
- throws IOException, GeneralSecurityException {
- @SuppressWarnings("synthetic-access")
- KeyPairResourceParser proxy = PROXY_HOLDER.get();
- return (proxy != null) && proxy.canExtractKeyPairs(resourceKey, lines);
- }
- };
-
- private static final Map<String, KeyPairPEMResourceParser> BY_OID_MAP = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- private static final Map<String, KeyPairPEMResourceParser> BY_ALGORITHM_MAP = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- private static final AtomicReference<KeyPairResourceParser> PROXY_HOLDER = new AtomicReference<>(KeyPairResourceParser.EMPTY);
-
- static {
- registerPEMResourceParser(RSAPEMResourceKeyPairParser.INSTANCE);
- registerPEMResourceParser(DSSPEMResourceKeyPairParser.INSTANCE);
- registerPEMResourceParser(ECDSAPEMResourceKeyPairParser.INSTANCE);
- registerPEMResourceParser(PKCS8PEMResourceKeyPairParser.INSTANCE);
- }
-
- private PEMResourceParserUtils() {
- throw new UnsupportedOperationException("No instance");
- }
-
- public static void registerPEMResourceParser(KeyPairPEMResourceParser parser) {
- Objects.requireNonNull(parser, "No parser to register");
- synchronized (BY_OID_MAP) {
- BY_OID_MAP.put(ValidateUtils.checkNotNullAndNotEmpty(parser.getAlgorithmIdentifier(), "No OID value"), parser);
- }
-
- synchronized (BY_ALGORITHM_MAP) {
- BY_ALGORITHM_MAP.put(ValidateUtils.checkNotNullAndNotEmpty(parser.getAlgorithm(), "No algorithm value"), parser);
- // Use a copy in order to avoid concurrent modifications
- PROXY_HOLDER.set(KeyPairResourceParser.aggregate(new ArrayList<>(BY_ALGORITHM_MAP.values())));
- }
- }
-
- public static KeyPairPEMResourceParser getPEMResourceParserByOid(String oid) {
- if (GenericUtils.isEmpty(oid)) {
- return null;
- }
-
- synchronized (BY_OID_MAP) {
- return BY_OID_MAP.get(oid);
- }
- }
-
- public static KeyPairPEMResourceParser getPEMResourceParserByAlgorithm(String algorithm) {
- if (GenericUtils.isEmpty(algorithm)) {
- return null;
- }
-
- synchronized (BY_ALGORITHM_MAP) {
- return BY_ALGORITHM_MAP.get(algorithm);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
deleted file mode 100644
index b333f23..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
+++ /dev/null
@@ -1,156 +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.loader.pem;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-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.der.ASN1Object;
-import org.apache.sshd.common.util.io.der.DERParser;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class PKCS8PEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParser {
- // Not exactly according to standard but good enough
- public static final String BEGIN_MARKER = "BEGIN PRIVATE KEY";
- public static final List<String> BEGINNERS =
- Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
-
- public static final String END_MARKER = "END PRIVATE KEY";
- public static final List<String> ENDERS =
- Collections.unmodifiableList(Collections.singletonList(END_MARKER));
-
- public static final String PKCS8_FORMAT = "PKCS#8";
-
- public static final PKCS8PEMResourceKeyPairParser INSTANCE = new PKCS8PEMResourceKeyPairParser();
-
- public PKCS8PEMResourceKeyPairParser() {
- super(PKCS8_FORMAT, PKCS8_FORMAT, BEGINNERS, ENDERS);
- }
-
- @Override
- public Collection<KeyPair> extractKeyPairs(
- String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
- // Save the data before getting the algorithm OID since we will need it
- byte[] encBytes = IoUtils.toByteArray(stream);
- List<Integer> oidAlgorithm = getPKCS8AlgorithmIdentifier(encBytes);
- PrivateKey prvKey = decodePEMPrivateKeyPKCS8(oidAlgorithm, encBytes, passwordProvider);
- PublicKey pubKey = ValidateUtils.checkNotNull(KeyUtils.recoverPublicKey(prvKey),
- "Failed to recover public key of OID=%s", oidAlgorithm);
- KeyPair kp = new KeyPair(pubKey, prvKey);
- return Collections.singletonList(kp);
- }
-
- public static PrivateKey decodePEMPrivateKeyPKCS8(
- List<Integer> oidAlgorithm, byte[] keyBytes, FilePasswordProvider passwordProvider)
- throws GeneralSecurityException {
- ValidateUtils.checkNotNullAndNotEmpty(oidAlgorithm, "No PKCS8 algorithm OID");
- return decodePEMPrivateKeyPKCS8(GenericUtils.join(oidAlgorithm, '.'), keyBytes, passwordProvider);
- }
-
- public static PrivateKey decodePEMPrivateKeyPKCS8(
- String oid, byte[] keyBytes, FilePasswordProvider passwordProvider)
- throws GeneralSecurityException {
- KeyPairPEMResourceParser parser =
- PEMResourceParserUtils.getPEMResourceParserByOid(
- ValidateUtils.checkNotNullAndNotEmpty(oid, "No PKCS8 algorithm OID"));
- if (parser == null) {
- throw new NoSuchAlgorithmException("decodePEMPrivateKeyPKCS8(" + oid + ") unknown algorithm identifier");
- }
-
- String algorithm = ValidateUtils.checkNotNullAndNotEmpty(parser.getAlgorithm(), "No parser algorithm");
- KeyFactory factory = SecurityUtils.getKeyFactory(algorithm);
- PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
- return factory.generatePrivate(keySpec);
- }
-
- public static List<Integer> getPKCS8AlgorithmIdentifier(byte[] input) throws IOException {
- try (DERParser parser = new DERParser(input)) {
- return getPKCS8AlgorithmIdentifier(parser);
- }
- }
-
- /**
- * According to the standard:
- * <PRE><CODE>
- * PrivateKeyInfo ::= SEQUENCE {
- * version Version,
- * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
- * privateKey PrivateKey,
- * attributes [0] IMPLICIT Attributes OPTIONAL
- * }
- *
- * Version ::= INTEGER
- * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
- * PrivateKey ::= OCTET STRING
- * Attributes ::= SET OF Attribute
- * AlgorithmIdentifier ::= SEQUENCE {
- * algorithm OBJECT IDENTIFIER,
- * parameters ANY DEFINED BY algorithm OPTIONAL
- * }
- * </CODE></PRE>
- * @param parser The {@link DERParser} to use
- * @return The PKCS8 algorithm OID
- * @throws IOException If malformed data
- * @see #getPKCS8AlgorithmIdentifier(ASN1Object)
- */
- public static List<Integer> getPKCS8AlgorithmIdentifier(DERParser parser) throws IOException {
- return getPKCS8AlgorithmIdentifier(parser.readObject());
- }
-
- public static List<Integer> getPKCS8AlgorithmIdentifier(ASN1Object privateKeyInfo) throws IOException {
- try (DERParser parser = privateKeyInfo.createParser()) {
- // Skip version
- ASN1Object versionObject = parser.readObject();
- if (versionObject == null) {
- throw new StreamCorruptedException("No version");
- }
-
- ASN1Object privateKeyAlgorithm = parser.readObject();
- if (privateKeyAlgorithm == null) {
- throw new StreamCorruptedException("No private key algorithm");
- }
-
- try (DERParser oidParser = privateKeyAlgorithm.createParser()) {
- ASN1Object oid = oidParser.readObject();
- return oid.asOID();
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
deleted file mode 100644
index d760aaf..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
+++ /dev/null
@@ -1,142 +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.loader.pem;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StreamCorruptedException;
-import java.math.BigInteger;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.RSAPrivateCrtKeySpec;
-import java.security.spec.RSAPrivateKeySpec;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.der.ASN1Object;
-import org.apache.sshd.common.util.io.der.ASN1Type;
-import org.apache.sshd.common.util.io.der.DERParser;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class RSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParser {
- // Not exactly according to standard but good enough
- public static final String BEGIN_MARKER = "BEGIN RSA PRIVATE KEY";
- public static final List<String> BEGINNERS =
- Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
-
- public static final String END_MARKER = "END RSA PRIVATE KEY";
- public static final List<String> ENDERS =
- Collections.unmodifiableList(Collections.singletonList(END_MARKER));
-
- /**
- * @see <A HREF="https://tools.ietf.org/html/rfc3279#section-2.3.1">RFC-3279 section 2.3.1</A>
- */
- public static final String RSA_OID = "1.2.840.113549.1.1.1";
-
- public static final RSAPEMResourceKeyPairParser INSTANCE = new RSAPEMResourceKeyPairParser();
-
- public RSAPEMResourceKeyPairParser() {
- super(KeyUtils.RSA_ALGORITHM, RSA_OID, BEGINNERS, ENDERS);
- }
-
- @Override
- public Collection<KeyPair> extractKeyPairs(
- String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
- KeyPair kp = decodeRSAKeyPair(SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM), stream, false);
- return Collections.singletonList(kp);
- }
-
- /**
- * <p>The ASN.1 syntax for the private key as per RFC-3447 section A.1.2:</P>
- * <pre><code>
- * RSAPrivateKey ::= SEQUENCE {
- * version Version,
- * modulus INTEGER, -- n
- * publicExponent INTEGER, -- e
- * privateExponent INTEGER, -- d
- * prime1 INTEGER, -- p
- * prime2 INTEGER, -- q
- * exponent1 INTEGER, -- d mod (p-1)
- * exponent2 INTEGER, -- d mod (q-1)
- * coefficient INTEGER, -- (inverse of q) mod p
- * otherPrimeInfos OtherPrimeInfos OPTIONAL
- * }
- * </code></pre>
- * @param kf The {@link KeyFactory} To use to generate the keys
- * @param s The {@link InputStream} containing the encoded bytes
- * @param okToClose <code>true</code> if the method may close the input
- * stream regardless of success or failure
- * @return The recovered {@link KeyPair}
- * @throws IOException If failed to read or decode the bytes
- * @throws GeneralSecurityException If failed to generate the keys
- */
- public static KeyPair decodeRSAKeyPair(KeyFactory kf, InputStream s, boolean okToClose)
- throws IOException, GeneralSecurityException {
- ASN1Object sequence;
- try (DERParser parser = new DERParser(NoCloseInputStream.resolveInputStream(s, okToClose))) {
- sequence = parser.readObject();
- }
-
- if (!ASN1Type.SEQUENCE.equals(sequence.getObjType())) {
- throw new IOException("Invalid DER: not a sequence: " + sequence.getObjType());
- }
-
- try (DERParser parser = sequence.createParser()) {
- // Skip version
- ASN1Object versionObject = parser.readObject();
- if (versionObject == null) {
- throw new StreamCorruptedException("No version");
- }
-
- // as per RFC-3447 section A.1.2
- BigInteger version = versionObject.asInteger();
- if (!BigInteger.ZERO.equals(version)) {
- throw new StreamCorruptedException("Multi-primes N/A");
- }
-
- BigInteger modulus = parser.readObject().asInteger();
- BigInteger publicExp = parser.readObject().asInteger();
- PublicKey pubKey = kf.generatePublic(new RSAPublicKeySpec(modulus, publicExp));
-
- BigInteger privateExp = parser.readObject().asInteger();
- BigInteger primeP = parser.readObject().asInteger();
- BigInteger primeQ = parser.readObject().asInteger();
- BigInteger primeExponentP = parser.readObject().asInteger();
- BigInteger primeExponentQ = parser.readObject().asInteger();
- BigInteger crtCoef = parser.readObject().asInteger();
- RSAPrivateKeySpec prvSpec = new RSAPrivateCrtKeySpec(
- modulus, publicExp, privateExp, primeP, primeQ, primeExponentP, primeExponentQ, crtCoef);
- PrivateKey prvKey = kf.generatePrivate(prvSpec);
- return new KeyPair(pubKey, prvKey);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/digest/BaseDigest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/digest/BaseDigest.java b/sshd-core/src/main/java/org/apache/sshd/common/digest/BaseDigest.java
deleted file mode 100644
index a5ef6f9..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/digest/BaseDigest.java
+++ /dev/null
@@ -1,156 +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.digest;
-
-import java.security.MessageDigest;
-import java.util.Objects;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Base class for Digest algorithms based on the JCE provider.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BaseDigest implements Digest {
-
- private final String algorithm;
- private final int bsize;
- private int h;
- private String s;
- private MessageDigest md;
-
- /**
- * Create a new digest using the given algorithm and block size.
- * The initialization and creation of the underlying {@link MessageDigest}
- * object will be done in the {@link #init()} method.
- *
- * @param algorithm the JCE algorithm to use for this digest
- * @param bsize the block size of this digest
- */
- public BaseDigest(String algorithm, int bsize) {
- this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm");
- ValidateUtils.checkTrue(bsize > 0, "Invalid block size: %d", bsize);
- this.bsize = bsize;
- }
-
- @Override
- public final String getAlgorithm() {
- return algorithm;
- }
-
- @Override
- public int getBlockSize() {
- return bsize;
- }
-
- @Override
- public void init() throws Exception {
- this.md = SecurityUtils.getMessageDigest(getAlgorithm());
- }
-
- @Override
- public void update(byte[] data) throws Exception {
- update(data, 0, NumberUtils.length(data));
- }
-
- @Override
- public void update(byte[] data, int start, int len) throws Exception {
- Objects.requireNonNull(md, "Digest not initialized").update(data, start, len);
- }
-
- /**
- * @return The current {@link MessageDigest} - may be {@code null} if {@link #init()} not called
- */
- protected MessageDigest getMessageDigest() {
- return md;
- }
-
- @Override
- public byte[] digest() throws Exception {
- return Objects.requireNonNull(md, "Digest not initialized").digest();
- }
-
- @Override
- public int hashCode() {
- synchronized (this) {
- if (h == 0) {
- h = Objects.hashCode(getAlgorithm()) + getBlockSize();
- if (h == 0) {
- h = 1;
- }
- }
- }
-
- return h;
- }
-
- @Override
- public int compareTo(Digest that) {
- if (that == null) {
- return -1; // push null(s) to end
- } else if (this == that) {
- return 0;
- }
-
- String thisAlg = getAlgorithm();
- String thatAlg = that.getAlgorithm();
- int nRes = GenericUtils.safeCompare(thisAlg, thatAlg, false);
- if (nRes != 0) {
- return nRes; // debug breakpoint
- }
-
- nRes = Integer.compare(this.getBlockSize(), that.getBlockSize());
- if (nRes != 0) {
- return nRes; // debug breakpoint
- }
-
- return 0;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (obj == this) {
- return true;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
-
- int nRes = compareTo((Digest) obj);
- return nRes == 0;
- }
-
- @Override
- public String toString() {
- synchronized (this) {
- if (s == null) {
- s = getClass().getSimpleName() + "[" + getAlgorithm() + ":" + getBlockSize() + "]";
- }
- }
-
- return s;
- }
-}
[17/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java
deleted file mode 100644
index c9d876a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java
+++ /dev/null
@@ -1,94 +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.signature;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.NamedResource;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * Manage the list of named factories for <code>Signature</code>.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface SignatureFactoriesManager {
- /**
- * @return The list of named <code>Signature</code> factories
- */
- List<NamedFactory<Signature>> getSignatureFactories();
-
- default String getSignatureFactoriesNameList() {
- return NamedResource.getNames(getSignatureFactories());
- }
-
- default List<String> getSignatureFactoriesNames() {
- return NamedResource.getNameList(getSignatureFactories());
- }
-
- void setSignatureFactories(List<NamedFactory<Signature>> factories);
-
- default void setSignatureFactoriesNameList(String names) {
- setSignatureFactoriesNames(GenericUtils.split(names, ','));
- }
-
- default void setSignatureFactoriesNames(String... names) {
- setSignatureFactoriesNames(GenericUtils.isEmpty((Object[]) names) ? Collections.emptyList() : Arrays.asList(names));
- }
-
- default void setSignatureFactoriesNames(Collection<String> names) {
- BuiltinSignatures.ParseResult result = BuiltinSignatures.parseSignatureList(names);
- @SuppressWarnings({ "rawtypes", "unchecked" })
- List<NamedFactory<Signature>> factories =
- (List) ValidateUtils.checkNotNullAndNotEmpty(result.getParsedFactories(), "No supported signature factories: %s", names);
- Collection<String> unsupported = result.getUnsupportedFactories();
- ValidateUtils.checkTrue(GenericUtils.isEmpty(unsupported), "Unsupported signature factories found: %s", unsupported);
- setSignatureFactories(factories);
- }
-
- /**
- * Attempts to use the primary manager's signature factories if not {@code null}/empty,
- * otherwise uses the secondary ones (regardless of whether there are any...)
- *
- * @param primary The primary {@link SignatureFactoriesManager}
- * @param secondary The secondary {@link SignatureFactoriesManager}
- * @return The resolved signature factories - may be {@code null}/empty
- * @see #getSignatureFactories(SignatureFactoriesManager)
- */
- static List<NamedFactory<Signature>> resolveSignatureFactories(
- SignatureFactoriesManager primary, SignatureFactoriesManager secondary) {
- List<NamedFactory<Signature>> factories = getSignatureFactories(primary);
- return GenericUtils.isEmpty(factories) ? getSignatureFactories(secondary) : factories;
- }
-
- /**
- * @param manager The {@link SignatureFactoriesManager} instance - ignored if {@code null}
- * @return The associated list of named <code>Signature</code> factories or {@code null} if
- * no manager instance
- */
- static List<NamedFactory<Signature>> getSignatureFactories(SignatureFactoriesManager manager) {
- return (manager == null) ? null : manager.getSignatureFactories();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
deleted file mode 100644
index 0881714..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
+++ /dev/null
@@ -1,31 +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.signature;
-
-import org.apache.sshd.common.BuiltinFactory;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-// CHECKSTYLE:OFF
-public interface SignatureFactory extends BuiltinFactory<Signature> {
- // nothing extra
-}
-//CHECKSTYLE:ON
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
deleted file mode 100644
index ad1d37e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
+++ /dev/null
@@ -1,89 +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.signature;
-
-import java.math.BigInteger;
-import java.security.PublicKey;
-import java.security.interfaces.RSAKey;
-import java.util.Map;
-
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.ValidateUtils;
-
-/**
- * RSA <code>Signature</code>
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="https://tools.ietf.org/html/rfc4253#section-6.6">RFC4253 section 6.6</A>
- */
-public class SignatureRSA extends AbstractSignature {
- public static final String DEFAULT_ALGORITHM = "SHA1withRSA";
-
- private int verifierSignatureSize = -1;
-
- public SignatureRSA() {
- super(DEFAULT_ALGORITHM);
- }
-
- protected SignatureRSA(String algorithm) {
- super(algorithm);
- }
-
- /**
- * @return The expected number of bytes in the signature - non-positive
- * if not initialized or not intended to be used for verification
- */
- protected int getVerifierSignatureSize() {
- return verifierSignatureSize;
- }
-
- @Override
- public void initVerifier(PublicKey key) throws Exception {
- super.initVerifier(key);
- RSAKey rsaKey = ValidateUtils.checkInstanceOf(key, RSAKey.class, "Not an RSA key");
- verifierSignatureSize = getVerifierSignatureSize(rsaKey);
- }
-
- public static int getVerifierSignatureSize(RSAKey key) {
- BigInteger modulus = key.getModulus();
- return (modulus.bitLength() + Byte.SIZE - 1) / Byte.SIZE;
- }
-
- @Override
- public boolean verify(byte[] sig) throws Exception {
- byte[] data = sig;
- Map.Entry<String, byte[]> encoding = extractEncodedSignature(data);
- if (encoding != null) {
- String keyType = encoding.getKey();
- ValidateUtils.checkTrue(KeyPairProvider.SSH_RSA.equals(keyType), "Mismatched key type: %s", keyType);
- data = encoding.getValue();
- }
-
- int expectedSize = getVerifierSignatureSize();
- ValidateUtils.checkTrue(expectedSize > 0, "Signature verification size has not been initialized");
- // Pad with zero if value is trimmed
- if (data.length < expectedSize) {
- byte[] pad = new byte[expectedSize];
- System.arraycopy(data, 0, pad, pad.length - data.length, data.length);
- data = pad;
- }
-
- return doVerify(data);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/signature/package.html
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/package.html b/sshd-core/src/main/java/org/apache/sshd/common/signature/package.html
deleted file mode 100644
index d1b16e4..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/package.html
+++ /dev/null
@@ -1,25 +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.
--->
-<html>
-<head>
-</head>
-<body>
-
-<a href="{@docRoot}/org/apache/sshd/common/signature/Signature.html"><code>Signature</code></a> implementations.
-
-</body>
-</html>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/EventListenerUtils.java
deleted file mode 100644
index 1620bad..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/EventListenerUtils.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.util;
-
-import java.lang.reflect.Proxy;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EventListener;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeSet;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class EventListenerUtils {
- /**
- * A special "comparator" whose only purpose is to ensure
- * there are no same references in a listener's set - to be used
- * in conjunction with a {@code TreeSet} as its comparator
- */
- @SuppressWarnings("checkstyle:anoninnerlength")
- public static final Comparator<EventListener> LISTENER_INSTANCE_COMPARATOR = (l1, l2) -> {
- if (l1 == l2) {
- return 0;
- } else if (l1 == null) {
- return 1;
- } else if (l2 == null) {
- return -1;
- }
-
- Class<?> c1 = l1.getClass();
- Class<?> c2 = l2.getClass();
- boolean checkHashCodes = true;
- if (Proxy.isProxyClass(c1)) {
- if (Proxy.isProxyClass(c2)) {
- checkHashCodes = false; // cannot call hashCode on a proxy
- } else {
- return 1;
- }
- } else if (Proxy.isProxyClass(c2)) {
- return -1;
- }
-
- if (checkHashCodes) {
- int nRes = Integer.compare(l1.hashCode(), l2.hashCode());
- if (nRes != 0) {
- return nRes;
- }
- }
-
- int nRes = Integer.compare(System.identityHashCode(l1), System.identityHashCode(l2));
- if (nRes != 0) {
- return nRes;
- }
-
- if (c1 != c2) {
- return c1.getName().compareTo(c2.getName());
- }
-
- String s1 = Objects.toString(l1.toString(), "");
- String s2 = Objects.toString(l2.toString(), "");
- nRes = s1.compareTo(s2);
- if (nRes != 0) {
- return nRes;
- }
- throw new UnsupportedOperationException("Ran out of options to compare instance of " + s1 + " vs. " + s2);
- };
-
- private EventListenerUtils() {
- throw new UnsupportedOperationException("No instance");
- }
-
- /**
- * @param <L> Type of {@link SshdEventListener} contained in the set
- * @param listeners The listeners to pre-add to the create set - ignored
- * if (@code null}/empty
- * @return A (synchronized) {@link Set} for containing the listeners ensuring
- * that if same listener instance is added repeatedly only <U>one</U>
- * instance is actually contained
- */
- public static <L extends SshdEventListener> Set<L> synchronizedListenersSet(Collection<? extends L> listeners) {
- Set<L> s = EventListenerUtils.synchronizedListenersSet();
- if (GenericUtils.size(listeners) > 0) {
- s.addAll(listeners);
- }
-
- return s;
- }
-
- /**
- * @param <L> Type of {@link SshdEventListener} contained in the set
- * @return A (synchronized) {@link Set} for containing the listeners ensuring
- * that if same listener instance is added repeatedly only <U>one</U>
- * instance is actually contained
- * @see #LISTENER_INSTANCE_COMPARATOR
- */
- public static <L extends SshdEventListener> Set<L> synchronizedListenersSet() {
- return Collections.synchronizedSet(new TreeSet<L>(LISTENER_INSTANCE_COMPARATOR));
- }
-
- /**
- * Provides proxy wrapper around an {@link Iterable} container of listener
- * interface implementation. <b>Note:</b> a listener interface is one whose
- * invoked methods return <u>only</u> {@code void}.
- *
- * @param <T> Generic listener type
- * @param listenerType The expected listener <u>interface</u>
- * @param listeners An {@link Iterable} container of listeners to be invoked.
- * <p>
- * <b>Note(s):</b>
- * </p>
- * <ul>
- * <li><p>
- * The invocation order is same as the {@link Iterable} container
- * </p></li>
- *
- * <li><p>
- * If any of the invoked listener methods throws an exception, the
- * rest of the listener are <u>not</u> invoked and the exception is
- * propagated to the caller
- * </p></li>
- *
- * <li><p>
- * It is up to the <u>caller</u> to ensure that the container does
- * not change while the proxy is invoked
- * </p></li>
- * </ul>
- * @return A proxy wrapper implementing the same interface, but delegating
- * the calls to the container
- * @see #proxyWrapper(Class, ClassLoader, Iterable)
- */
- public static <T extends SshdEventListener> T proxyWrapper(Class<T> listenerType, Iterable<? extends T> listeners) {
- return proxyWrapper(listenerType, listenerType.getClassLoader(), listeners);
- }
-
- /**
- * Provides proxy wrapper around an {@link Iterable} container of listener
- * interface implementation. <b>Note:</b> a listener interface is one whose
- * invoked methods return <u>only</u> {@code void}.
- *
- * @param <T> Generic {@link SshdEventListener} type
- * @param listenerType The expected listener <u>interface</u>
- * @param loader The {@link ClassLoader} to use for the proxy
- * @param listeners An {@link Iterable} container of listeners to be invoked.
- * <p>
- * <b>Note(s):</b>
- * </p>
- * <ul>
- * <li><p>
- * The invocation order is same as the {@link Iterable} container
- * </p></li>
- *
- * <li><p>
- * If any of the invoked listener methods throws an exception, the
- * rest of the listener are <u>not</u> invoked and the exception is
- * propagated to the caller
- * </p></li>
- *
- * <li><p>
- * It is up to the <u>caller</u> to ensure that the container does
- * not change while the proxy is invoked
- * </p></li>
- * </ul>
- * @return A proxy wrapper implementing the same interface, but delegating
- * the calls to the container
- * @throws IllegalArgumentException if <tt>listenerType</tt> is not an interface
- * or a {@code null} container has been provided
- * @see #proxyWrapper(Class, ClassLoader, Iterable)
- */
- public static <T extends SshdEventListener> T proxyWrapper(Class<T> listenerType, ClassLoader loader, final Iterable<? extends T> listeners) {
- Objects.requireNonNull(listenerType, "No listener type specified");
- ValidateUtils.checkTrue(listenerType.isInterface(), "Target proxy is not an interface: %s", listenerType.getSimpleName());
- Objects.requireNonNull(listeners, "No listeners container provided");
-
- Object wrapper = Proxy.newProxyInstance(loader, new Class<?>[]{listenerType}, (proxy, method, args) -> {
- Throwable err = null;
- for (T l : listeners) {
- try {
- method.invoke(l, args);
- } catch (Throwable t) {
- Throwable e = GenericUtils.peelException(t);
- err = GenericUtils.accumulateException(err, e);
- }
- }
-
- if (err != null) {
- throw err;
- }
-
- return null; // we assume always void return value...
- });
- return listenerType.cast(wrapper);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/EventNotifier.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/EventNotifier.java b/sshd-core/src/main/java/org/apache/sshd/common/util/EventNotifier.java
deleted file mode 100644
index c041f1b..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/EventNotifier.java
+++ /dev/null
@@ -1,35 +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.util;
-
-/**
- * Notify about the occurrence of an event
- *
- * @param <E> type of event being notified
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface EventNotifier<E> {
- /**
- * @param event The event
- * @throws Exception If failed to process the event notification
- */
- void notifyEvent(E event) throws Exception;
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
deleted file mode 100644
index c924d1e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
+++ /dev/null
@@ -1,915 +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.util;
-
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.UndeclaredThrowableException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableMap;
-import java.util.NavigableSet;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.concurrent.ExecutionException;
-import java.util.function.BinaryOperator;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-import java.util.stream.Collector;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
-import javax.management.MBeanException;
-import javax.management.ReflectionException;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class GenericUtils {
-
- public static final byte[] EMPTY_BYTE_ARRAY = {};
- public static final char[] EMPTY_CHAR_ARRAY = {};
- public static final String[] EMPTY_STRING_ARRAY = {};
- public static final Object[] EMPTY_OBJECT_ARRAY = {};
-
- /**
- * A value indicating a {@code null} value - to be used as a placeholder
- * where {@code null}s are not allowed
- */
- public static final Object NULL = new Object();
-
- /**
- * The complement of {@link String#CASE_INSENSITIVE_ORDER}
- */
- public static final Comparator<String> CASE_SENSITIVE_ORDER = (s1, s2) -> {
- if (s1 == s2) {
- return 0;
- } else {
- return s1.compareTo(s2);
- }
- };
-
- public static final String QUOTES = "\"'";
-
- @SuppressWarnings("rawtypes")
- private static final Supplier CASE_INSENSITIVE_MAP_FACTORY = () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
- private GenericUtils() {
- throw new UnsupportedOperationException("No instance");
- }
-
- public static String trimToEmpty(String s) {
- if (s == null) {
- return "";
- } else {
- return s.trim();
- }
- }
-
- public static String replaceWhitespaceAndTrim(String s) {
- if (s != null) {
- s = s.replace('\t', ' ');
- }
-
- return trimToEmpty(s);
- }
-
- /**
- * @param s The {@link String} value to calculate the hash code on - may
- * be {@code null}/empty in which case a value of zero is returned
- * @return The calculated hash code
- * @see #hashCode(String, Boolean)
- */
- public static int hashCode(String s) {
- return hashCode(s, null);
- }
-
- /**
- * @param s The {@link String} value to calculate the hash code on - may
- * be {@code null}/empty in which case a value of zero is returned
- * @param useUppercase Whether to convert the string to uppercase, lowercase
- * or not at all:
- * <UL>
- * <LI>{@code null} - no conversion</LI>
- * <LI>{@link Boolean#TRUE} - get hash code of uppercase</LI>
- * <LI>{@link Boolean#FALSE} - get hash code of lowercase</LI>
- * </UL>
- * @return The calculated hash code
- */
- public static int hashCode(String s, Boolean useUppercase) {
- if (isEmpty(s)) {
- return 0;
- } else if (useUppercase == null) {
- return s.hashCode();
- } else if (useUppercase.booleanValue()) {
- return s.toUpperCase().hashCode();
- } else {
- return s.toLowerCase().hashCode();
- }
- }
-
- public static int safeCompare(String s1, String s2, boolean caseSensitive) {
- if (isSameReference(s1, s2)) {
- return 0;
- } else if (s1 == null) {
- return +1; // push null(s) to end
- } else if (s2 == null) {
- return -1; // push null(s) to end
- } else if (caseSensitive) {
- return s1.compareTo(s2);
- } else {
- return s1.compareToIgnoreCase(s2);
- }
- }
-
- public static <T> boolean isSameReference(T o1, T o2) {
- return o1 == o2;
- }
-
- public static int length(CharSequence cs) {
- return cs == null ? 0 : cs.length();
- }
-
- public static boolean isEmpty(CharSequence cs) {
- return length(cs) <= 0;
- }
-
- public static boolean isNotEmpty(CharSequence cs) {
- return !isEmpty(cs);
- }
-
- public static int indexOf(CharSequence cs, char c) {
- int len = length(cs);
- for (int pos = 0; pos < len; pos++) {
- char ch = cs.charAt(pos);
- if (ch == c) {
- return pos;
- }
- }
-
- return -1;
- }
-
- public static int lastIndexOf(CharSequence cs, char c) {
- int len = length(cs);
- for (int pos = len - 1; pos >= 0; pos--) {
- char ch = cs.charAt(pos);
- if (ch == c) {
- return pos;
- }
- }
-
- return -1;
- }
-
- // a List would be better, but we want to be compatible with String.split(...)
- public static String[] split(String s, char ch) {
- if (isEmpty(s)) {
- return EMPTY_STRING_ARRAY;
- }
-
- int lastPos = 0;
- int curPos = s.indexOf(ch);
- if (curPos < 0) {
- return new String[]{s};
- }
-
- Collection<String> values = new LinkedList<>();
- do {
- String v = s.substring(lastPos, curPos);
- values.add(v);
-
- // skip separator
- lastPos = curPos + 1;
- if (lastPos >= s.length()) {
- break;
- }
-
- curPos = s.indexOf(ch, lastPos);
- if (curPos < lastPos) {
- break; // no more separators
- }
- } while (curPos < s.length());
-
- // check if any leftovers
- if (lastPos < s.length()) {
- String v = s.substring(lastPos);
- values.add(v);
- }
-
- return values.toArray(new String[values.size()]);
- }
-
- public static <T> String join(T[] values, char ch) {
- return join(isEmpty(values) ? Collections.<T>emptyList() : Arrays.asList(values), ch);
- }
-
- public static String join(Iterable<?> iter, char ch) {
- return join((iter == null) ? null : iter.iterator(), ch);
- }
-
- public static String join(Iterator<?> iter, char ch) {
- if ((iter == null) || (!iter.hasNext())) {
- return "";
- }
-
- StringBuilder sb = new StringBuilder();
- do { // we already asked hasNext...
- Object o = iter.next();
- if (sb.length() > 0) {
- sb.append(ch);
- }
- sb.append(Objects.toString(o));
- } while (iter.hasNext());
-
- return sb.toString();
- }
-
- public static <T> String join(T[] values, CharSequence sep) {
- return join(isEmpty(values) ? Collections.<T>emptyList() : Arrays.asList(values), sep);
- }
-
- public static String join(Iterable<?> iter, CharSequence sep) {
- return join((iter == null) ? null : iter.iterator(), sep);
- }
-
- public static String join(Iterator<?> iter, CharSequence sep) {
- if ((iter == null) || (!iter.hasNext())) {
- return "";
- }
-
- StringBuilder sb = new StringBuilder();
- do { // we already asked hasNext...
- Object o = iter.next();
- if (sb.length() > 0) {
- sb.append(sep);
- }
- sb.append(Objects.toString(o));
- } while (iter.hasNext());
-
- return sb.toString();
- }
-
- public static int size(Collection<?> c) {
- return c == null ? 0 : c.size();
- }
-
- public static boolean isEmpty(Collection<?> c) {
- return (c == null) || c.isEmpty();
- }
-
- public static boolean isNotEmpty(Collection<?> c) {
- return !isEmpty(c);
- }
-
- public static int size(Map<?, ?> m) {
- return m == null ? 0 : m.size();
- }
-
- public static boolean isEmpty(Map<?, ?> m) {
- return (m == null) || m.isEmpty();
- }
-
- public static boolean isNotEmpty(Map<?, ?> m) {
- return !isEmpty(m);
- }
-
- @SafeVarargs
- public static <T> int length(T... a) {
- return a == null ? 0 : a.length;
- }
-
- public static <T> boolean isEmpty(Iterable<? extends T> iter) {
- if (iter == null) {
- return true;
- } else if (iter instanceof Collection<?>) {
- return isEmpty((Collection<?>) iter);
- } else {
- return isEmpty(iter.iterator());
- }
- }
-
- public static <T> boolean isNotEmpty(Iterable<? extends T> iter) {
- return !isEmpty(iter);
- }
-
- public static <T> boolean isEmpty(Iterator<? extends T> iter) {
- return iter == null || !iter.hasNext();
- }
-
- public static <T> boolean isNotEmpty(Iterator<? extends T> iter) {
- return !isEmpty(iter);
- }
-
- @SafeVarargs
- public static <T> boolean isEmpty(T... a) {
- return length(a) <= 0;
- }
-
- public static int length(char[] chars) {
- return (chars == null) ? 0 : chars.length;
- }
-
- public static boolean isEmpty(char[] chars) {
- return length(chars) <= 0;
- }
-
- /**
- * Compares 2 character arrays - <B>Note:</B> {@code null} and empty
- * are considered <U>equal</U>
- *
- * @param c1 1st array
- * @param c2 2nd array
- * @return Negative is 1st array comes first in lexicographical order,
- * positive if 2nd array comes first and zero if equal
- */
- public static int compare(char[] c1, char[] c2) {
- int l1 = length(c1);
- int l2 = length(c2);
- int cmpLen = Math.min(l1, l2);
- for (int index = 0; index < cmpLen; index++) {
- char c11 = c1[index];
- char c22 = c2[index];
- int nRes = Character.compare(c11, c22);
- if (nRes != 0) {
- return nRes;
- }
- }
-
- int nRes = Integer.compare(l1, l2);
- if (nRes != 0) {
- return nRes;
- }
-
- return 0;
- }
-
- @SafeVarargs // there is no EnumSet.of(...) so we have to provide our own
- public static <E extends Enum<E>> Set<E> of(E... values) {
- return of(isEmpty(values) ? Collections.emptySet() : Arrays.asList(values));
- }
-
- public static <E extends Enum<E>> Set<E> of(Collection<? extends E> values) {
- if (isEmpty(values)) {
- return Collections.emptySet();
- }
-
- Set<E> result = null;
- for (E v : values) {
- /*
- * A trick to compensate for the fact that we do not have
- * the enum Class to invoke EnumSet.noneOf
- */
- if (result == null) {
- result = EnumSet.of(v);
- } else {
- result.add(v);
- }
- }
-
- return result;
- }
-
- public static <T> boolean containsAny(Collection<? extends T> coll, Iterable<? extends T> values) {
- if (isEmpty(coll)) {
- return false;
- }
-
- for (T v : values) {
- if (coll.contains(v)) {
- return true;
- }
- }
-
- return false;
- }
-
- public static <T> void forEach(Iterable<T> values, Consumer<T> consumer) {
- if (isNotEmpty(values)) {
- values.forEach(consumer);
- }
- }
-
- public static <T, U> List<U> map(Collection<T> values, Function<? super T, ? extends U> mapper) {
- return stream(values).map(mapper).collect(Collectors.toList());
- }
-
- public static <T, U> NavigableSet<U> mapSort(
- Collection<T> values, Function<? super T, ? extends U> mapper, Comparator<U> comparator) {
- return stream(values).map(mapper).collect(toSortedSet(comparator));
- }
-
- public static <T, K, U> NavigableMap<K, U> toSortedMap(
- Iterable<T> values, Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, Comparator<K> comparator) {
- return stream(values).collect(toSortedMap(keyMapper, valueMapper, comparator));
- }
-
- public static <T, K, U> Collector<T, ?, NavigableMap<K, U>> toSortedMap(
- Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, Comparator<K> comparator) {
- return Collectors.toMap(keyMapper, valueMapper, throwingMerger(), () -> new TreeMap<>(comparator));
- }
-
- private static <T> BinaryOperator<T> throwingMerger() {
- return (u, v) -> {
- throw new IllegalStateException(String.format("Duplicate key %s", u));
- };
- }
-
- public static <T> Collector<T, ?, NavigableSet<T>> toSortedSet(Comparator<T> comparator) {
- return Collectors.toCollection(() -> new TreeSet<>(comparator));
- }
-
- public static <T> Stream<T> stream(Iterable<T> values) {
- if (isEmpty(values)) {
- return Stream.empty();
- } else if (values instanceof Collection<?>) {
- return ((Collection<T>) values).stream();
- } else {
- return StreamSupport.stream(values.spliterator(), false);
- }
- }
-
- @SafeVarargs
- public static <T> List<T> unmodifiableList(T... values) {
- return unmodifiableList(asList(values));
- }
-
- public static <T> List<T> unmodifiableList(Collection<? extends T> values) {
- if (isEmpty(values)) {
- return Collections.emptyList();
- } else {
- return Collections.unmodifiableList(new ArrayList<>(values));
- }
- }
-
- public static <T> List<T> unmodifiableList(Stream<T> values) {
- return unmodifiableList(values.collect(Collectors.toList()));
- }
-
- @SafeVarargs
- public static <T> List<T> asList(T... values) {
- return isEmpty(values) ? Collections.emptyList() : Arrays.asList(values);
- }
-
- @SafeVarargs
- public static <T> Set<T> asSet(T... values) {
- return new HashSet<>(asList(values));
- }
-
- @SafeVarargs
- public static <V extends Comparable<V>> NavigableSet<V> asSortedSet(V... values) {
- return asSortedSet(Comparator.naturalOrder(), values);
- }
-
- public static <V extends Comparable<V>> NavigableSet<V> asSortedSet(Collection<? extends V> values) {
- return asSortedSet(Comparator.naturalOrder(), values);
- }
-
- /**
- * @param <V> The element type
- * @param comp The (non-{@code null}) {@link Comparator} to use
- * @param values The values to be added (ignored if {@code null})
- * @return A {@link NavigableSet} containing the values (if any) sorted
- * using the provided comparator
- */
- @SafeVarargs
- public static <V> NavigableSet<V> asSortedSet(Comparator<? super V> comp, V... values) {
- return asSortedSet(comp, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
- }
-
- /**
- * @param <V> The element type
- * @param comp The (non-{@code null}) {@link Comparator} to use
- * @param values The values to be added (ignored if {@code null}/empty)
- * @return A {@link NavigableSet} containing the values (if any) sorted
- * using the provided comparator
- */
- public static <V> NavigableSet<V> asSortedSet(Comparator<? super V> comp, Collection<? extends V> values) {
- NavigableSet<V> set = new TreeSet<>(Objects.requireNonNull(comp, "No comparator"));
- if (size(values) > 0) {
- set.addAll(values);
- }
- return set;
- }
-
- /**
- * @param <V> Type of mapped value
- * @return A {@link Supplier} that returns a <U>new</U> {@link NavigableMap}
- * whenever its {@code get()} method is invoked
- */
- @SuppressWarnings("unchecked")
- public static <V> Supplier<NavigableMap<String, V>> caseInsensitiveMap() {
- return CASE_INSENSITIVE_MAP_FACTORY;
- }
-
- /**
- * Flips between keys and values of an input map
- *
- * @param <K> Original map key type
- * @param <V> Original map value type
- * @param <M> Flipped map type
- * @param map The original map to flip
- * @param mapCreator The creator of the target map
- * @param allowDuplicates Whether to ignore duplicates on flip
- * @return The flipped map result
- * @throws IllegalArgumentException if <tt>allowDuplicates</tt> is {@code false}
- * and a duplicate value found in the original map.
- */
- public static <K, V, M extends Map<V, K>> M flipMap(
- Map<? extends K, ? extends V> map, Supplier<? extends M> mapCreator, boolean allowDuplicates) {
- M result = Objects.requireNonNull(mapCreator.get(), "No map created");
- map.forEach((key, value) -> {
- K prev = result.put(value, key);
- if ((prev != null) && (!allowDuplicates)) {
- ValidateUtils.throwIllegalArgumentException("Multiple values for key=%s: current=%s, previous=%s", value, key, prev);
- }
- });
-
- return result;
- }
-
- @SafeVarargs
- public static <K, V, M extends Map<K, V>> M mapValues(
- Function<? super V, ? extends K> keyMapper, Supplier<? extends M> mapCreator, V... values) {
- return mapValues(keyMapper, mapCreator, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
- }
-
- /**
- * Creates a map out of a group of values
- *
- * @param <K> The key type
- * @param <V> The value type
- * @param <M> The result {@link Map} type
- * @param keyMapper The {@link Function} that generates a key for a given value.
- * If the returned key is {@code null} then the value is not mapped
- * @param mapCreator The {@link Supplier} used to create/retrieve the result map - provided
- * non-empty group of values
- * @param values The values to be mapped
- * @return The resulting {@link Map} - <B>Note:</B> no validation is made to ensure
- * that 2 (or more) values are not mapped to the same key
- */
- public static <K, V, M extends Map<K, V>> M mapValues(
- Function<? super V, ? extends K> keyMapper, Supplier<? extends M> mapCreator, Collection<? extends V> values) {
- M map = mapCreator.get();
- for (V v : values) {
- K k = keyMapper.apply(v);
- if (k == null) {
- continue; // debug breakpoint
- }
- map.put(k, v);
- }
-
- return map;
- }
-
- @SafeVarargs
- public static <T> T findFirstMatchingMember(Predicate<? super T> acceptor, T... values) {
- return findFirstMatchingMember(acceptor, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
- }
-
- public static <T> T findFirstMatchingMember(Predicate<? super T> acceptor, Collection<? extends T> values) {
- List<T> matches = selectMatchingMembers(acceptor, values);
- return GenericUtils.isEmpty(matches) ? null : matches.get(0);
- }
-
- /**
- * Returns a list of all the values that were accepted by a predicate
- *
- * @param <T> The type of value being evaluated
- * @param acceptor The {@link Predicate} to consult whether a member is selected
- * @param values The values to be scanned
- * @return A {@link List} of all the values that were accepted by the predicate
- */
- @SafeVarargs
- public static <T> List<T> selectMatchingMembers(Predicate<? super T> acceptor, T... values) {
- return selectMatchingMembers(acceptor, isEmpty(values) ? Collections.emptyList() : Arrays.asList(values));
- }
-
- /**
- * Returns a list of all the values that were accepted by a predicate
- *
- * @param <T> The type of value being evaluated
- * @param acceptor The {@link Predicate} to consult whether a member is selected
- * @param values The values to be scanned
- * @return A {@link List} of all the values that were accepted by the predicate
- */
- public static <T> List<T> selectMatchingMembers(Predicate<? super T> acceptor, Collection<? extends T> values) {
- return GenericUtils.stream(values)
- .filter(acceptor)
- .collect(Collectors.toList());
- }
-
- /**
- * @param s The {@link CharSequence} to be checked
- * @return If the sequence contains any of the {@link #QUOTES}
- * on <U>both</U> ends, then they are stripped, otherwise
- * nothing is done
- * @see #stripDelimiters(CharSequence, char)
- */
- public static CharSequence stripQuotes(CharSequence s) {
- if (isEmpty(s)) {
- return s;
- }
-
- for (int index = 0; index < QUOTES.length(); index++) {
- char delim = QUOTES.charAt(index);
- CharSequence v = stripDelimiters(s, delim);
- if (v != s) { // if stripped one don't continue
- return v;
- }
- }
-
- return s;
- }
-
- /**
- * @param s The {@link CharSequence} to be checked
- * @param delim The expected delimiter
- * @return If the sequence contains the delimiter on <U>both</U> ends,
- * then it is are stripped, otherwise nothing is done
- */
- public static CharSequence stripDelimiters(CharSequence s, char delim) {
- if (isEmpty(s) || (s.length() < 2)) {
- return s;
- }
-
- int lastPos = s.length() - 1;
- if ((s.charAt(0) != delim) || (s.charAt(lastPos) != delim)) {
- return s;
- } else {
- return s.subSequence(1, lastPos);
- }
- }
-
- public static RuntimeException toRuntimeException(Throwable t) {
- return toRuntimeException(t, true);
- }
-
- /**
- * Converts a thrown generic exception to a {@link RuntimeException}
- *
- * @param t The original thrown exception
- * @param peelThrowable Whether to determine the root cause by "peeling"
- * any enclosing exceptions
- * @return The thrown cause if already a runtime exception, otherwise a
- * runtime exception of the resolved exception as its cause
- * @see #peelException(Throwable)
- */
- public static RuntimeException toRuntimeException(Throwable t, boolean peelThrowable) {
- Throwable e = peelThrowable ? peelException(t) : t;
- if (e instanceof RuntimeException) {
- return (RuntimeException) e;
- }
-
- return new RuntimeException(e);
- }
-
- /**
- * Attempts to get to the "effective" exception being thrown,
- * by taking care of some known exceptions that wrap the original thrown
- * one.
- *
- * @param t The original {@link Throwable} - ignored if {@code null}
- * @return The effective exception - same as input if not a wrapper
- */
- public static Throwable peelException(Throwable t) {
- // NOTE: check order is important - e.g., InvocationTargetException extends ReflectiveOperationException
- if (t == null) {
- return t;
- } else if (t instanceof UndeclaredThrowableException) {
- Throwable wrapped = ((UndeclaredThrowableException) t).getUndeclaredThrowable();
- // according to the Javadoc it may be null, in which case 'getCause'
- // might contain the information we need
- if (wrapped != null) {
- return peelException(wrapped);
- }
-
- wrapped = t.getCause();
- if (wrapped != t) { // make sure it is a real cause
- return peelException(wrapped);
- }
- } else if (t instanceof InvocationTargetException) {
- Throwable target = ((InvocationTargetException) t).getTargetException();
- if (target != null) {
- return peelException(target);
- }
- } else if (t instanceof ReflectionException) {
- Throwable target = ((ReflectionException) t).getTargetException();
- if (target != null) {
- return peelException(target);
- }
- } else if (t instanceof ExecutionException) {
- Throwable wrapped = resolveExceptionCause(t);
- if (wrapped != null) {
- return peelException(wrapped);
- }
- } else if (t instanceof MBeanException) {
- Throwable target = ((MBeanException) t).getTargetException();
- if (target != null) {
- return peelException(target);
- }
- }
-
- return t; // no special handling required or available
- }
-
- /**
- * @param t The original {@link Throwable} - ignored if {@code null}
- * @return If {@link Throwable#getCause()} is non-{@code null} then
- * the cause, otherwise the original exception - {@code null} if
- * the original exception was {@code null}
- */
- public static Throwable resolveExceptionCause(Throwable t) {
- if (t == null) {
- return t;
- }
-
- Throwable c = t.getCause();
- if (c == null) {
- return t;
- } else {
- return c;
- }
- }
-
- /**
- * Used to "accumulate" exceptions of the <U>same type</U>. If the
- * current exception is {@code null} then the new one becomes the current,
- * otherwise the new one is added as a <U>suppressed</U> exception to the
- * current one
- *
- * @param <T> The exception type
- * @param current The current exception
- * @param extra The extra/new exception
- * @return The resolved exception
- * @see Throwable#addSuppressed(Throwable)
- */
- public static <T extends Throwable> T accumulateException(T current, T extra) {
- if (current == null) {
- return extra;
- }
-
- if ((extra == null) || (extra == current)) {
- return current;
- }
-
- current.addSuppressed(extra);
- return current;
- }
-
- public static IOException toIOException(Throwable e) {
- if (e instanceof IOException) {
- return (IOException) e;
- } else {
- return new IOException(e);
- }
- }
-
- /**
- * Wraps a value into a {@link Supplier}
- * @param <T> Type of value being supplied
- * @param value The value to be supplied
- * @return The supplier wrapper
- */
- public static <T> Supplier<T> supplierOf(T value) {
- return () -> value;
- }
-
- /**
- * Resolves to an always non-{@code null} iterator
- *
- * @param <T> Type of value being iterated
- * @param iterable The {@link Iterable} instance
- * @return A non-{@code null} iterator which may be empty if no iterable
- * instance or no iterator returned from it
- * @see #iteratorOf(Iterator)
- */
- public static <T> Iterator<T> iteratorOf(Iterable<T> iterable) {
- return iteratorOf((iterable == null) ? null : iterable.iterator());
- }
-
- /**
- * @param <B> Generic base class
- * @param <D> Generic child class
- * @return An identity {@link Function} that returns its input child class as a base class
- */
- public static <B, D extends B> Function<D, B> downcast() {
- return t -> t;
- }
-
- /**
- * Resolves to an always non-{@code null} iterator
- *
- * @param <T> Type of value being iterated
- * @param iter The {@link Iterator} instance
- * @return A non-{@code null} iterator which may be empty if no iterator instance
- * @see Collections#emptyIterator()
- */
- public static <T> Iterator<T> iteratorOf(Iterator<T> iter) {
- return (iter == null) ? Collections.emptyIterator() : iter;
- }
-
- public static <U, V> Iterable<V> wrapIterable(Iterable<? extends U> iter, Function<? super U, ? extends V> mapper) {
- return () -> wrapIterator(iter, mapper);
- }
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- public static <U, V> Iterator<V> wrapIterator(Iterable<? extends U> iter, Function<? super U, ? extends V> mapper) {
- return (Iterator) stream(iter).map(mapper).iterator();
- }
-
- public static <U, V> Iterator<V> wrapIterator(Iterator<? extends U> iter, Function<? super U, ? extends V> mapper) {
- Iterator<? extends U> iterator = iteratorOf(iter);
- return new Iterator<V>() {
- @Override
- public boolean hasNext() {
- return iterator.hasNext();
- }
-
- @Override
- public V next() {
- U value = iterator.next();
- return mapper.apply(value);
- }
- };
- }
-
- /**
- * Wraps a group of {@link Supplier}s of {@link Iterable} instances into a "unified"
- * {@link Iterable} of their values, in the same order as the suppliers - i.e., once the values
- * from a specific supplier are exhausted, the next one is consulted, and so on, until all
- * suppliers have been consulted
- *
- * @param <T> Type of value being iterated
- * @param providers The providers - ignored if {@code null} (i.e., return an empty iterable instance)
- * @return The wrapping instance
- */
- public static <T> Iterable<T> multiIterableSuppliers(Iterable<? extends Supplier<? extends Iterable<? extends T>>> providers) {
- return () -> stream(providers).<T>flatMap(s -> stream(s.get())).map(Function.identity()).iterator();
- }
-
- public static <K, V> MapBuilder<K, V> mapBuilder() {
- return new MapBuilder<>();
- }
-
- public static <K, V> MapBuilder<K, V> mapBuilder(Comparator<K> comparator) {
- return new MapBuilder<>(comparator);
- }
-
- public static class MapBuilder<K, V> {
- private Map<K, V> map;
-
- public MapBuilder() {
- this.map = new LinkedHashMap<>();
- }
-
- public MapBuilder(Comparator<? super K> comparator) {
- this.map = new TreeMap<>(comparator);
- }
-
- public MapBuilder<K, V> put(K k, V v) {
- map.put(k, v);
- return this;
- }
-
- public Map<K, V> build() {
- return map;
- }
-
- public Map<K, V> immutable() {
- return Collections.unmodifiableMap(map);
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java b/sshd-core/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
deleted file mode 100644
index 8f18bfe..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/IgnoringEmptyMap.java
+++ /dev/null
@@ -1,128 +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.util;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * A dummy map that ignores all {@code put/remove} calls
- *
- * @param <K> Key type
- * @param <V> Value type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class IgnoringEmptyMap<K, V> implements Map<K, V> {
- @SuppressWarnings("rawtypes")
- private static final IgnoringEmptyMap INSTANCE = new IgnoringEmptyMap();
-
- public IgnoringEmptyMap() {
- super();
- }
-
- @Override
- public int size() {
- return 0;
- }
-
- @Override
- public boolean isEmpty() {
- return true;
- }
-
- @Override
- public boolean containsValue(Object value) {
- Objects.requireNonNull(value, "No value provided");
- return false;
- }
-
- @Override
- public boolean containsKey(Object key) {
- Objects.requireNonNull(key, "No key provided");
- return false;
- }
-
- @Override
- public V get(Object key) {
- Objects.requireNonNull(key, "No key provided");
- return null;
- }
-
- @Override
- public V put(K key, V value) {
- Objects.requireNonNull(key, "No key provided");
- Objects.requireNonNull(value, "No value provided");
- return null;
- }
-
- @Override
- public V remove(Object key) {
- Objects.requireNonNull(key, "No key provided");
- return null;
- }
-
- @Override
- public void putAll(Map<? extends K, ? extends V> m) {
- // ignored
- }
-
- @Override
- public void clear() {
- // ignored
- }
-
- @Override
- public Set<K> keySet() {
- return Collections.emptySet();
- }
-
- @Override
- public Collection<V> values() {
- return Collections.emptyList();
- }
-
- @Override
- public boolean equals(Object o) {
- return o instanceof IgnoringEmptyMap<?, ?>;
- }
-
- @Override
- public int hashCode() {
- return 0;
- }
-
- @Override
- public String toString() {
- return "{}";
- }
-
- @Override
- public Set<Entry<K, V>> entrySet() {
- return Collections.emptySet();
- }
-
- @SuppressWarnings("unchecked")
- public static <K, V> IgnoringEmptyMap<K, V> getInstance() {
- return INSTANCE;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java b/sshd-core/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java
deleted file mode 100644
index 490abb0..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/Int2IntFunction.java
+++ /dev/null
@@ -1,66 +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.util;
-
-import java.util.function.IntUnaryOperator;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class Int2IntFunction {
- private Int2IntFunction() {
- throw new UnsupportedOperationException("No instance");
- }
-
- public static IntUnaryOperator sub(int delta) {
- return add(0 - delta);
- }
-
- public static IntUnaryOperator add(int delta) {
- if (delta == 0) {
- return IntUnaryOperator.identity();
- } else {
- return value -> value + delta;
- }
- }
-
- public static IntUnaryOperator mul(int factor) {
- if (factor == 0) {
- return constant(0);
- } else if (factor == 1) {
- return IntUnaryOperator.identity();
- } else {
- return value -> value * factor;
- }
- }
-
- public static IntUnaryOperator constant(int v) {
- return value -> v;
- }
-
- public static IntUnaryOperator div(int factor) {
- if (factor == 1) {
- return IntUnaryOperator.identity();
- } else {
- ValidateUtils.checkTrue(factor != 0, "Zero division factor");
- return value -> value / factor;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/Invoker.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/Invoker.java b/sshd-core/src/main/java/org/apache/sshd/common/util/Invoker.java
deleted file mode 100644
index 71cbebd..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/Invoker.java
+++ /dev/null
@@ -1,116 +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.util;
-
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * The complement to the {@code Callable} interface - accepts one argument
- * and possibly throws somethind
- *
- * @param <ARG> Argument type
- * @param <RET> Return type
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface Invoker<ARG, RET> {
- RET invoke(ARG arg) throws Throwable;
-
- static <ARG> Invoker<ARG, Void> wrapAll(Collection<? extends Invoker<? super ARG, ?>> invokers) {
- return arg -> {
- invokeAll(arg, invokers);
- return null;
- };
- }
-
- /**
- * Invokes <U>all</U> the instances ignoring the return value. Any
- * intermediate exceptions are accumulated and thrown at the end.
- *
- * @param <ARG> Argument type
- * @param arg The argument to pass to the {@link #invoke(Object)} method
- * @param invokers The invokers to scan - ignored if {@code null}/empty
- * (also ignores {@code null} members)
- * @throws Throwable If invocation failed
- */
- static <ARG> void invokeAll(ARG arg, Collection<? extends Invoker<? super ARG, ?>> invokers) throws Throwable {
- if (GenericUtils.isEmpty(invokers)) {
- return;
- }
-
- Throwable err = null;
- for (Invoker<? super ARG, ?> i : invokers) {
- if (i == null) {
- continue;
- }
-
- try {
- i.invoke(arg);
- } catch (Throwable t) {
- err = GenericUtils.accumulateException(err, t);
- }
- }
-
- if (err != null) {
- throw err;
- }
- }
-
- static <ARG> Invoker<ARG, Void> wrapFirst(Collection<? extends Invoker<? super ARG, ?>> invokers) {
- return arg -> {
- Map.Entry<Invoker<? super ARG, ?>, Throwable> result = invokeTillFirstFailure(arg, invokers);
- if (result != null) {
- throw result.getValue();
- }
- return null;
- };
- }
-
- /**
- * Invokes all instances until 1st failure (if any)
- *
- * @param <ARG> Argument type
- * @param arg The argument to pass to the {@link #invoke(Object)} method
- * @param invokers The invokers to scan - ignored if {@code null}/empty
- * (also ignores {@code null} members)
- * @return A {@link SimpleImmutableEntry} representing the <U>first</U> failed
- * invocation - {@code null} if all were successful (or none invoked).
- */
- static <ARG> SimpleImmutableEntry<Invoker<? super ARG, ?>, Throwable> invokeTillFirstFailure(ARG arg, Collection<? extends Invoker<? super ARG, ?>> invokers) {
- if (GenericUtils.isEmpty(invokers)) {
- return null;
- }
-
- for (Invoker<? super ARG, ?> i : invokers) {
- if (i == null) {
- continue;
- }
-
- try {
- i.invoke(arg);
- } catch (Throwable t) {
- return new SimpleImmutableEntry<>(i, t);
- }
- }
-
- return null;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/MapEntryUtils.java
deleted file mode 100644
index 6bcc93a..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/MapEntryUtils.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.util;
-
-import java.util.Comparator;
-import java.util.Map;
-
-/**
- * Represents an un-modifiable pair of values
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class MapEntryUtils {
- @SuppressWarnings({"rawtypes", "unchecked"})
- private static final Comparator<Map.Entry<Comparable, ?>> BY_KEY_COMPARATOR = (o1, o2) -> {
- Comparable k1 = o1.getKey();
- Comparable k2 = o2.getKey();
- return k1.compareTo(k2);
- };
-
- private MapEntryUtils() {
- throw new UnsupportedOperationException("No instance");
- }
-
- /**
- * @param <K> The {@link Comparable} key type
- * @param <V> The associated entry value
- * @return A {@link Comparator} for {@link java.util.Map.Entry}-ies that
- * compares the key values
- */
- @SuppressWarnings({ "unchecked", "rawtypes" })
- public static <K extends Comparable<K>, V> Comparator<Map.Entry<K, V>> byKeyEntryComparator() {
- return (Comparator) BY_KEY_COMPARATOR;
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/NumberUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/NumberUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/NumberUtils.java
deleted file mode 100644
index b1aed93..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/NumberUtils.java
+++ /dev/null
@@ -1,313 +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.util;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.IntStream;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class NumberUtils {
- /**
- * A {@link List} of all the {@link Class} types used to represent the
- * primitive numerical values
- */
- public static final List<Class<?>> NUMERIC_PRIMITIVE_CLASSES =
- GenericUtils.unmodifiableList(
- Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE,
- Float.TYPE, Double.TYPE
- );
-
- /**
- * A {@link List} containing all the pure powers of 2 for a {@code long}
- * value. The value at index <I>n</I> is 2 to the power of <I>n</I>
- */
- public static final List<Long> POWERS_OF_TWO =
- GenericUtils.unmodifiableList(IntStream.range(0, 64)
- .mapToObj(i -> 1L << i));
-
- private NumberUtils() {
- throw new UnsupportedOperationException("No instance");
- }
-
- public static boolean isPowerOf2(long value) {
- for (Long l : POWERS_OF_TWO) {
- if (value == l) {
- return true;
- }
- }
-
- return false;
- }
-
- public static long getNextPowerOf2(long value) {
- long j = 1L;
- while (j < value) {
- j <<= 1;
- }
- return j;
- }
-
- public static int getNextPowerOf2(int value) {
- int j = 1;
- while (j < value) {
- j <<= 1;
- }
- return j;
- }
-
- public static int hashCode(long... values) {
- return Arrays.hashCode(values);
- }
-
- public static int hashCode(int... values) {
- return Arrays.hashCode(values);
- }
-
- public static int hashCode(byte... values) {
- return Arrays.hashCode(values);
- }
-
- public static int hashCode(byte[] a, int offset, int len) {
- if (len == 0) {
- return 0;
- }
-
- int result = 1;
- for (int pos = offset, count = 0; count < len; pos++, count++) {
- byte element = a[pos];
- result = 31 * result + element;
- }
-
- return result;
- }
-
- public static int diffOffset(byte[] a1, int startPos1, byte[] a2, int startPos2, int len) {
- for (int pos1 = startPos1, pos2 = startPos2, count = 0; count < len; pos1++, pos2++, count++) {
- byte v1 = a1[pos1];
- byte v2 = a2[pos2];
- if (v1 != v2) {
- return count;
- }
- }
-
- return -1;
- }
-
- /**
- * @param clazz The {@link Class} to examine - ignored if {@code null}
- * @return If the class is a {@link Number} or one of the primitive numerical types
- * @see #NUMERIC_PRIMITIVE_CLASSES
- */
- public static boolean isNumericClass(Class<?> clazz) {
- if (clazz == null) {
- return false;
- }
-
- // turns out that the primitive types are not assignable to Number
- if (Number.class.isAssignableFrom(clazz)) {
- return true;
- }
-
- return NUMERIC_PRIMITIVE_CLASSES.indexOf(clazz) >= 0;
- }
-
- /**
- * Converts a {@link Number} into an {@link Integer} if not already such
- *
- * @param n The {@link Number} - ignored if {@code null}
- * @return The equivalent {@link Integer} value
- */
- public static Integer toInteger(Number n) {
- if (n == null) {
- return null;
- } else if (n instanceof Integer) {
- return (Integer) n;
- } else {
- return n.intValue();
- }
- }
-
- public static String join(CharSequence separator, long... values) {
- if (NumberUtils.isEmpty(values)) {
- return "";
- }
-
- StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
- for (long v : values) {
- if (sb.length() > 0) {
- sb.append(separator);
- }
- sb.append(v);
- }
-
- return sb.toString();
- }
-
- public static String join(char separator, long... values) {
- if (NumberUtils.isEmpty(values)) {
- return "";
- }
-
- StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
- for (long v : values) {
- if (sb.length() > 0) {
- sb.append(separator);
- }
- sb.append(v);
- }
-
- return sb.toString();
- }
-
- public static String join(CharSequence separator, boolean unsigned, byte... values) {
- if (NumberUtils.isEmpty(values)) {
- return "";
- }
-
- StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
- for (byte v : values) {
- if (sb.length() > 0) {
- sb.append(separator);
- }
- sb.append(unsigned ? (v & 0xFF) : v);
- }
-
- return sb.toString();
- }
-
- public static String join(char separator, boolean unsigned, byte... values) {
- if (NumberUtils.isEmpty(values)) {
- return "";
- }
-
- StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
- for (byte v : values) {
- if (sb.length() > 0) {
- sb.append(separator);
- }
- sb.append(unsigned ? (v & 0xFF) : v);
- }
-
- return sb.toString();
- }
-
- public static String join(CharSequence separator, int... values) {
- if (NumberUtils.isEmpty(values)) {
- return "";
- }
-
- StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
- for (int v : values) {
- if (sb.length() > 0) {
- sb.append(separator);
- }
- sb.append(v);
- }
-
- return sb.toString();
- }
-
- public static String join(char separator, int... values) {
- if (NumberUtils.isEmpty(values)) {
- return "";
- }
-
- StringBuilder sb = new StringBuilder(values.length * Byte.SIZE);
- for (int v : values) {
- if (sb.length() > 0) {
- sb.append(separator);
- }
- sb.append(v);
- }
-
- return sb.toString();
- }
-
- public static boolean isEmpty(byte[] a) {
- return NumberUtils.length(a) <= 0;
- }
-
- public static boolean isEmpty(int[] a) {
- return NumberUtils.length(a) <= 0;
- }
-
- public static boolean isEmpty(long[] a) {
- return NumberUtils.length(a) <= 0;
- }
-
- public static int length(byte... a) {
- return a == null ? 0 : a.length;
- }
-
- public static int length(int... a) {
- return a == null ? 0 : a.length;
- }
-
- public static int length(long... a) {
- return a == null ? 0 : a.length;
- }
-
- public static List<Integer> asList(int... values) {
- int len = length(values);
- if (len <= 0) {
- return Collections.emptyList();
- }
-
- List<Integer> l = new ArrayList<>(len);
- for (int v : values) {
- l.add(v);
- }
-
- return l;
- }
-
- /**
- * Checks if optional sign and all others are '0'-'9'
- * @param cs The {@link CharSequence} to check
- * @return {@code true} if valid integer number
- */
- public static boolean isIntegerNumber(CharSequence cs) {
- if (GenericUtils.isEmpty(cs)) {
- return false;
- }
-
- for (int index = 0; index < cs.length(); index++) {
- char c = cs.charAt(0);
- if ((c >= '0') && (c <= '9')) {
- continue;
- }
-
- if ((c == '+') || (c == '-')) {
- if (index == 0) {
- continue;
- }
- }
-
- return false;
- }
-
- return true;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java b/sshd-core/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java
deleted file mode 100644
index 23884a2..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/ObjectBuilder.java
+++ /dev/null
@@ -1,38 +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.util;
-
-import java.util.function.Supplier;
-
-/**
- * A generic builder interface
- *
- * @param <T> Type of object being built
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface ObjectBuilder<T> extends Supplier<T> {
- @Override
- default T get() {
- return build();
- }
-
- T build();
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/OsUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/OsUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/OsUtils.java
deleted file mode 100644
index f0b2c7e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/OsUtils.java
+++ /dev/null
@@ -1,257 +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.util;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Operating system dependent utility methods.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class OsUtils {
-
- /**
- * Property that can be used to override the reported value from {@link #getCurrentUser()}.
- * If not set then "user.name" system property is used
- */
- public static final String CURRENT_USER_OVERRIDE_PROP = "org.apache.sshd.currentUser";
-
- /**
- * Property that can be used to override the reported value from {@link #getJavaVersion()}.
- * If not set then "java.version" system property is used
- */
- public static final String JAVA_VERSION_OVERRIDE_PROP = "org.apache.sshd.javaVersion";
-
- /**
- * Property that can be used to override the reported value from {@link #isWin32()}.
- * If not set then "os.name" system property is used
- */
- public static final String OS_TYPE_OVERRIDE_PROP = "org.apache.sshd.osType";
-
- public static final String WINDOWS_SHELL_COMMAND_NAME = "cmd.exe";
- public static final String LINUX_SHELL_COMMAND_NAME = "/bin/sh";
-
- public static final String ROOT_USER = "root";
-
- public static final List<String> LINUX_COMMAND =
- Collections.unmodifiableList(Arrays.asList(LINUX_SHELL_COMMAND_NAME, "-i", "-l"));
- public static final List<String> WINDOWS_COMMAND =
- Collections.unmodifiableList(Collections.singletonList(WINDOWS_SHELL_COMMAND_NAME));
-
- private static final AtomicReference<String> CURRENT_USER_HOLDER = new AtomicReference<>(null);
- private static final AtomicReference<VersionInfo> JAVA_VERSION_HOLDER = new AtomicReference<>(null);
- private static final AtomicReference<Boolean> OS_TYPE_HOLDER = new AtomicReference<>(null);
-
- private OsUtils() {
- throw new UnsupportedOperationException("No instance allowed");
- }
-
- /**
- * @return true if the host is a UNIX system (and not Windows).
- */
- public static boolean isUNIX() {
- return !isWin32();
- }
-
- /**
- * @return true if the host is Windows (and not UNIX).
- * @see #OS_TYPE_OVERRIDE_PROP
- * @see #setWin32(Boolean)
- */
- public static boolean isWin32() {
- Boolean typeValue;
- synchronized (OS_TYPE_HOLDER) {
- typeValue = OS_TYPE_HOLDER.get();
- if (typeValue != null) { // is it the 1st time
- return typeValue;
- }
-
- String value = System.getProperty(OS_TYPE_OVERRIDE_PROP, System.getProperty("os.name"));
- typeValue = GenericUtils.trimToEmpty(value).toLowerCase().contains("windows");
- OS_TYPE_HOLDER.set(typeValue);
- }
-
- return typeValue;
- }
-
- /**
- * Can be used to enforce Win32 or Linux report from {@link #isWin32()} or {@link #isUNIX()}
- * @param win32 The value to set - if {@code null} then O/S type is auto-detected
- * @see #isWin32()
- */
- public static void setWin32(Boolean win32) {
- synchronized (OS_TYPE_HOLDER) {
- OS_TYPE_HOLDER.set(win32);
- }
- }
-
- public static List<String> resolveDefaultInteractiveCommand() {
- return resolveInteractiveCommand(isWin32());
- }
-
- public static List<String> resolveInteractiveCommand(boolean isWin32) {
- if (isWin32) {
- return WINDOWS_COMMAND;
- } else {
- return LINUX_COMMAND;
- }
- }
-
- /**
- * Get current user name
- *
- * @return Current user
- * @see #CURRENT_USER_OVERRIDE_PROP
- */
- public static String getCurrentUser() {
- String username = null;
- synchronized (CURRENT_USER_HOLDER) {
- username = CURRENT_USER_HOLDER.get();
- if (username != null) { // have we already resolved it ?
- return username;
- }
-
- username = getCanonicalUser(System.getProperty(CURRENT_USER_OVERRIDE_PROP, System.getProperty("user.name")));
- ValidateUtils.checkNotNullAndNotEmpty(username, "No username available");
- CURRENT_USER_HOLDER.set(username);
- }
-
- return username;
- }
-
- /**
- * Remove {@code Windows} domain and/or group prefix as well as "(User);" suffix
- *
- * @param user The original username - ignored if {@code null}/empty
- * @return The canonical user - unchanged if {@code Unix} O/S
- */
- public static String getCanonicalUser(String user) {
- if (GenericUtils.isEmpty(user)) {
- return user;
- }
-
- // Windows owner sometime has the domain and/or group prepended to it
- if (isWin32()) {
- int pos = user.lastIndexOf('\\');
- if (pos > 0) {
- user = user.substring(pos + 1);
- }
-
- pos = user.indexOf(' ');
- if (pos > 0) {
- user = user.substring(0, pos).trim();
- }
- }
-
- return user;
- }
-
- /**
- * Attempts to resolve canonical group name for {@code Windows}
- *
- * @param group The original group name - used if not {@code null}/empty
- * @param user The owner name - sometimes it contains a group name
- * @return The canonical group name
- */
- public static String resolveCanonicalGroup(String group, String user) {
- if (isUNIX()) {
- return group;
- }
-
- // we reach this code only for Windows
- if (GenericUtils.isEmpty(group)) {
- int pos = GenericUtils.isEmpty(user) ? -1 : user.lastIndexOf('\\');
- return (pos > 0) ? user.substring(0, pos) : group;
- }
-
- int pos = group.indexOf(' ');
- return (pos < 0) ? group : group.substring(0, pos).trim();
- }
-
- /**
- * Can be used to programmatically set the username reported by {@link #getCurrentUser()}
- * @param username The username to set - if {@code null} then {@link #CURRENT_USER_OVERRIDE_PROP}
- * will be consulted
- */
- public static void setCurrentUser(String username) {
- synchronized (CURRENT_USER_HOLDER) {
- CURRENT_USER_HOLDER.set(username);
- }
- }
-
- /**
- * Resolves the reported Java version by consulting {@link #JAVA_VERSION_OVERRIDE_PROP}.
- * If not set, then "java.version" property is used
- * @return The resolved {@link VersionInfo} - never {@code null}
- * @see #setJavaVersion(VersionInfo)
- */
- public static VersionInfo getJavaVersion() {
- VersionInfo version;
- synchronized (JAVA_VERSION_HOLDER) {
- version = JAVA_VERSION_HOLDER.get();
- if (version != null) { // first time ?
- return version;
- }
-
- String value = System.getProperty(JAVA_VERSION_OVERRIDE_PROP, System.getProperty("java.version"));
- // e.g.: 1.7.5_30
- value = ValidateUtils.checkNotNullAndNotEmpty(value, "No configured Java version value").replace('_', '.');
- // clean up any non-digits - in case something like 1.6.8_25-b323
- for (int index = 0; index < value.length(); index++) {
- char ch = value.charAt(index);
- if ((ch == '.') || ((ch >= '0') && (ch <= '9'))) {
- continue;
- }
-
- value = value.substring(0, index);
- break;
- }
-
- version = ValidateUtils.checkNotNull(VersionInfo.parse(value), "No version parsed for %s", value);
- JAVA_VERSION_HOLDER.set(version);
- }
-
- return version;
- }
-
- /**
- * Set programmatically the reported Java version
- * @param version The version - if {@code null} then it will be automatically resolved
- */
- public static void setJavaVersion(VersionInfo version) {
- synchronized (JAVA_VERSION_HOLDER) {
- JAVA_VERSION_HOLDER.set(version);
- }
- }
-
- /**
- * @param path The original path
- * @return A path that can be compared with another one where case
- * sensitivity of the underlying O/S has been taken into account -
- * never {@code null}
- */
- public static String getComparablePath(String path) {
- String p = (path == null) ? "" : path;
- return isWin32() ? p.toLowerCase() : p;
- }
-}
[16/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/Readable.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/Readable.java b/sshd-core/src/main/java/org/apache/sshd/common/util/Readable.java
deleted file mode 100644
index 3075eb3..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/Readable.java
+++ /dev/null
@@ -1,53 +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.util;
-
-import java.nio.ByteBuffer;
-import java.util.Objects;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface Readable {
-
- int available();
-
- void getRawBytes(byte[] data, int offset, int len);
-
- /**
- * Wrap a {@link ByteBuffer} as a {@link Readable} instance
- *
- * @param buffer The {@link ByteBuffer} to wrap - never {@code null}
- * @return The {@link Readable} wrapper
- */
- static Readable readable(ByteBuffer buffer) {
- Objects.requireNonNull(buffer, "No buffer to wrap");
- return new Readable() {
- @Override
- public int available() {
- return buffer.remaining();
- }
-
- @Override
- public void getRawBytes(byte[] data, int offset, int len) {
- buffer.get(data, offset, len);
- }
- };
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
deleted file mode 100644
index 84a84f9..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
+++ /dev/null
@@ -1,53 +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.util;
-
-import java.lang.reflect.Field;
-import java.util.Collection;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class ReflectionUtils {
- public static final Function<Field, String> FIELD_NAME_EXTRACTOR = f -> (f == null) ? null : f.getName();
-
- private ReflectionUtils() {
- throw new UnsupportedOperationException("No instance");
- }
-
- public static Collection<Field> getMatchingFields(Class<?> clazz, Predicate<? super Field> acceptor) {
- return GenericUtils.selectMatchingMembers(acceptor, clazz.getFields());
- }
-
- public static Collection<Field> getMatchingDeclaredFields(Class<?> clazz, Predicate<? super Field> acceptor) {
- return GenericUtils.selectMatchingMembers(acceptor, clazz.getDeclaredFields());
- }
-
- public static boolean isClassAvailable(ClassLoader cl, String className) {
- try {
- cl.loadClass(className);
- return true;
- } catch (Throwable ignored) {
- return false;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/SelectorUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/SelectorUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/SelectorUtils.java
deleted file mode 100644
index 53bca90..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/SelectorUtils.java
+++ /dev/null
@@ -1,805 +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.util;
-
-import java.io.File;
-import java.nio.file.FileSystem;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.StringTokenizer;
-
-/**
- * <p>This is a utility class used by selectors and DirectoryScanner. The
- * functionality more properly belongs just to selectors, but unfortunately
- * DirectoryScanner exposed these as protected methods. Thus we have to
- * support any subclasses of DirectoryScanner that may access these methods.
- * </p>
- * <p>This is a Singleton.</p>
- *
- * @author Arnout J. Kuiper
- * <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a>
- * @author Magesh Umasankar
- * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a>
- * @version $Id$
- * @since 1.5
- */
-public final class SelectorUtils {
-
- public static final String PATTERN_HANDLER_PREFIX = "[";
-
- public static final String PATTERN_HANDLER_SUFFIX = "]";
-
- public static final String REGEX_HANDLER_PREFIX = "%regex" + PATTERN_HANDLER_PREFIX;
-
- public static final String ANT_HANDLER_PREFIX = "%ant" + PATTERN_HANDLER_PREFIX;
-
- /**
- * Private Constructor
- */
- private SelectorUtils() {
- throw new UnsupportedOperationException("No instance allowed");
- }
-
- /**
- * <p>Tests whether or not a given path matches the start of a given
- * pattern up to the first "**".</p>
- *
- * <p>This is not a general purpose test and should only be used if you
- * can live with false positives. For example, <code>pattern=**\a</code>
- * and <code>str=b</code> will yield <code>true</code>.</p>
- *
- * @param pattern The pattern to match against. Must not be
- * {@code null}.
- * @param str The path to match, as a String. Must not be
- * {@code null}.
- * @return whether or not a given path matches the start of a given
- * pattern up to the first "**".
- */
- public static boolean matchPatternStart(String pattern, String str) {
- return matchPatternStart(pattern, str, true);
- }
-
- /**
- * <p>Tests whether or not a given path matches the start of a given
- * pattern up to the first "**".</p>
- *
- * <p>This is not a general purpose test and should only be used if you
- * can live with false positives. For example, <code>pattern=**\a</code>
- * and <code>str=b</code> will yield <code>true</code>.</p>
- *
- * @param pattern The pattern to match against. Must not be
- * {@code null}.
- * @param str The path to match, as a String. Must not be
- * {@code null}.
- * @param isCaseSensitive Whether or not matching should be performed
- * case sensitively.
- * @return whether or not a given path matches the start of a given
- * pattern up to the first "**".
- */
- public static boolean matchPatternStart(String pattern, String str,
- boolean isCaseSensitive) {
- if (pattern.length() > (REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
- && pattern.startsWith(REGEX_HANDLER_PREFIX) && pattern.endsWith(PATTERN_HANDLER_SUFFIX)) {
- // FIXME: ICK! But we can't do partial matches for regex, so we have to reserve judgement until we have
- // a file to deal with, or we can definitely say this is an exclusion...
- return true;
- } else {
- if (pattern.length() > (ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
- && pattern.startsWith(ANT_HANDLER_PREFIX) && pattern.endsWith(PATTERN_HANDLER_SUFFIX)) {
- pattern =
- pattern.substring(ANT_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length());
- }
-
- String altStr = str.replace('\\', '/');
-
- return matchAntPathPatternStart(pattern, str, File.separator, isCaseSensitive)
- || matchAntPathPatternStart(pattern, altStr, "/", isCaseSensitive);
- }
- }
-
- private static boolean matchAntPathPatternStart(String pattern, String str, String separator, boolean isCaseSensitive) {
- // When str starts with a File.separator, pattern has to start with a
- // File.separator.
- // When pattern starts with a File.separator, str has to start with a
- // File.separator.
- if (str.startsWith(separator) != pattern.startsWith(separator)) {
- return false;
- }
-
- List<String> patDirs = tokenizePath(pattern, separator);
- List<String> strDirs = tokenizePath(str, separator);
-
- int patIdxStart = 0;
- int patIdxEnd = patDirs.size() - 1;
- int strIdxStart = 0;
- int strIdxEnd = strDirs.size() - 1;
-
- // up to first '**'
- while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
- String patDir = patDirs.get(patIdxStart);
- if (patDir.equals("**")) {
- break;
- }
- if (!match(patDir, strDirs.get(strIdxStart),
- isCaseSensitive)) {
- return false;
- }
- patIdxStart++;
- strIdxStart++;
- }
-
- // CHECKSTYLE:OFF
- if (strIdxStart > strIdxEnd) {
- // String is exhausted
- return true;
- } else {
- return patIdxStart <= patIdxEnd;
- }
- // CHECKSTYLE:ON
- }
-
- /**
- * Tests whether or not a given path matches a given pattern.
- *
- * @param pattern The pattern to match against. Must not be
- * {@code null}.
- * @param str The path to match, as a String. Must not be
- * {@code null}.
- * @return <code>true</code> if the pattern matches against the string,
- * or <code>false</code> otherwise.
- */
- public static boolean matchPath(String pattern, String str) {
- return matchPath(pattern, str, true);
- }
-
- /**
- * Tests whether or not a given path matches a given pattern.
- *
- * @param pattern The pattern to match against. Must not be
- * {@code null}.
- * @param str The path to match, as a String. Must not be
- * {@code null}.
- * @param isCaseSensitive Whether or not matching should be performed
- * case sensitively.
- * @return <code>true</code> if the pattern matches against the string,
- * or <code>false</code> otherwise.
- */
- public static boolean matchPath(String pattern, String str, boolean isCaseSensitive) {
- if (pattern.length() > (REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
- && pattern.startsWith(REGEX_HANDLER_PREFIX) && pattern.endsWith(PATTERN_HANDLER_SUFFIX)) {
- pattern = pattern.substring(REGEX_HANDLER_PREFIX.length(), pattern.length()
- - PATTERN_HANDLER_SUFFIX.length());
-
- return str.matches(pattern);
- } else {
- if (pattern.length() > (ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
- && pattern.startsWith(ANT_HANDLER_PREFIX) && pattern.endsWith(PATTERN_HANDLER_SUFFIX)) {
- pattern =
- pattern.substring(ANT_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length());
- }
-
- return matchAntPathPattern(pattern, str, isCaseSensitive);
- }
- }
-
- private static boolean matchAntPathPattern(String pattern, String str, boolean isCaseSensitive) {
- // When str starts with a File.separator, pattern has to start with a
- // File.separator.
- // When pattern starts with a File.separator, str has to start with a
- // File.separator.
- if (str.startsWith(File.separator) != pattern.startsWith(File.separator)) {
- return false;
- }
-
- List<String> patDirs = tokenizePath(pattern, File.separator);
- List<String> strDirs = tokenizePath(str, File.separator);
-
- int patIdxStart = 0;
- int patIdxEnd = patDirs.size() - 1;
- int strIdxStart = 0;
- int strIdxEnd = strDirs.size() - 1;
-
- // up to first '**'
- while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
- String patDir = patDirs.get(patIdxStart);
- if (patDir.equals("**")) {
- break;
- }
- if (!match(patDir, strDirs.get(strIdxStart),
- isCaseSensitive)) {
- patDirs = null;
- strDirs = null;
- return false;
- }
- patIdxStart++;
- strIdxStart++;
- }
- if (strIdxStart > strIdxEnd) {
- // String is exhausted
- for (int i = patIdxStart; i <= patIdxEnd; i++) {
- if (!patDirs.get(i).equals("**")) {
- patDirs = null;
- strDirs = null;
- return false;
- }
- }
- return true;
- } else {
- if (patIdxStart > patIdxEnd) {
- // String not exhausted, but pattern is. Failure.
- patDirs = null;
- strDirs = null;
- return false;
- }
- }
-
- // up to last '**'
- while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
- String patDir = patDirs.get(patIdxEnd);
- if (patDir.equals("**")) {
- break;
- }
- if (!match(patDir, strDirs.get(strIdxEnd),
- isCaseSensitive)) {
- patDirs = null;
- strDirs = null;
- return false;
- }
- patIdxEnd--;
- strIdxEnd--;
- }
- if (strIdxStart > strIdxEnd) {
- // String is exhausted
- for (int i = patIdxStart; i <= patIdxEnd; i++) {
- if (!patDirs.get(i).equals("**")) {
- patDirs = null;
- strDirs = null;
- return false;
- }
- }
- return true;
- }
-
- while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
- int patIdxTmp = -1;
- for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
- if (patDirs.get(i).equals("**")) {
- patIdxTmp = i;
- break;
- }
- }
- if (patIdxTmp == patIdxStart + 1) {
- // '**/**' situation, so skip one
- patIdxStart++;
- continue;
- }
- // Find the pattern between padIdxStart & padIdxTmp in str between
- // strIdxStart & strIdxEnd
- int patLength = patIdxTmp - patIdxStart - 1;
- int strLength = strIdxEnd - strIdxStart + 1;
- int foundIdx = -1;
- strLoop:
- for (int i = 0; i <= strLength - patLength; i++) {
- for (int j = 0; j < patLength; j++) {
- String subPat = patDirs.get(patIdxStart + j + 1);
- String subStr = strDirs.get(strIdxStart + i + j);
- if (!match(subPat, subStr, isCaseSensitive)) {
- continue strLoop;
- }
- }
-
- foundIdx = strIdxStart + i;
- break;
- }
-
- if (foundIdx == -1) {
- patDirs = null;
- strDirs = null;
- return false;
- }
-
- patIdxStart = patIdxTmp;
- strIdxStart = foundIdx + patLength;
- }
-
- for (int i = patIdxStart; i <= patIdxEnd; i++) {
- if (!patDirs.get(i).equals("**")) {
- patDirs = null;
- strDirs = null;
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Tests whether or not a string matches against a pattern.
- * The pattern may contain two special characters:<br>
- * '*' means zero or more characters<br>
- * '?' means one and only one character
- *
- * @param pattern The pattern to match against.
- * Must not be {@code null}.
- * @param str The string which must be matched against the pattern.
- * Must not be {@code null}.
- * @return <code>true</code> if the string matches against the pattern,
- * or <code>false</code> otherwise.
- */
- public static boolean match(String pattern, String str) {
- return match(pattern, str, true);
- }
-
- /**
- * Tests whether or not a string matches against a pattern.
- * The pattern may contain two special characters:<br>
- * '*' means zero or more characters<br>
- * '?' means one and only one character
- *
- * @param pattern The pattern to match against.
- * Must not be {@code null}.
- * @param str The string which must be matched against the pattern.
- * Must not be {@code null}.
- * @param isCaseSensitive Whether or not matching should be performed
- * case sensitively.
- * @return <code>true</code> if the string matches against the pattern,
- * or <code>false</code> otherwise.
- */
- @SuppressWarnings("PMD.AssignmentInOperand")
- public static boolean match(String pattern, String str, boolean isCaseSensitive) {
- char[] patArr = pattern.toCharArray();
- char[] strArr = str.toCharArray();
- int patIdxStart = 0;
- int patIdxEnd = patArr.length - 1;
- int strIdxStart = 0;
- int strIdxEnd = strArr.length - 1;
- char ch;
-
- boolean containsStar = false;
- for (char aPatArr : patArr) {
- if (aPatArr == '*') {
- containsStar = true;
- break;
- }
- }
-
- if (!containsStar) {
- // No '*'s, so we make a shortcut
- if (patIdxEnd != strIdxEnd) {
- return false; // Pattern and string do not have the same size
- }
- for (int i = 0; i <= patIdxEnd; i++) {
- ch = patArr[i];
- if ((ch != '?') && (!equals(ch, strArr[i], isCaseSensitive))) {
- return false; // Character mismatch
- }
- }
- return true; // String matches against pattern
- }
-
- if (patIdxEnd == 0) {
- return true; // Pattern contains only '*', which matches anything
- }
-
- // Process characters before first star
- // CHECKSTYLE:OFF
- while (((ch = patArr[patIdxStart]) != '*') && (strIdxStart <= strIdxEnd)) {
- if ((ch != '?') && (!equals(ch, strArr[strIdxStart], isCaseSensitive))) {
- return false; // Character mismatch
- }
- patIdxStart++;
- strIdxStart++;
- }
- // CHECKSTYLE:ON
-
- if (strIdxStart > strIdxEnd) {
- // All characters in the string are used. Check if only '*'s are
- // left in the pattern. If so, we succeeded. Otherwise failure.
- for (int i = patIdxStart; i <= patIdxEnd; i++) {
- if (patArr[i] != '*') {
- return false;
- }
- }
- return true;
- }
-
- // Process characters after last star
- // CHECKSTYLE:OFF
- while (((ch = patArr[patIdxEnd]) != '*') && (strIdxStart <= strIdxEnd)) {
- if ((ch != '?') && (!equals(ch, strArr[strIdxEnd], isCaseSensitive))) {
- return false; // Character mismatch
- }
- patIdxEnd--;
- strIdxEnd--;
- }
- // CHECKSTYLE:ON
-
- if (strIdxStart > strIdxEnd) {
- // All characters in the string are used. Check if only '*'s are
- // left in the pattern. If so, we succeeded. Otherwise failure.
- for (int i = patIdxStart; i <= patIdxEnd; i++) {
- if (patArr[i] != '*') {
- return false;
- }
- }
- return true;
- }
-
- // process pattern between stars. padIdxStart and patIdxEnd point always to a '*'.
- while ((patIdxStart != patIdxEnd) && (strIdxStart <= strIdxEnd)) {
- int patIdxTmp = -1;
- for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
- if (patArr[i] == '*') {
- patIdxTmp = i;
- break;
- }
- }
- if (patIdxTmp == patIdxStart + 1) {
- // Two stars next to each other, skip the first one.
- patIdxStart++;
- continue;
- }
- // Find the pattern between padIdxStart & padIdxTmp in str between
- // strIdxStart & strIdxEnd
- int patLength = patIdxTmp - patIdxStart - 1;
- int strLength = strIdxEnd - strIdxStart + 1;
- int foundIdx = -1;
- strLoop:
- for (int i = 0; i <= strLength - patLength; i++) {
- for (int j = 0; j < patLength; j++) {
- ch = patArr[patIdxStart + j + 1];
- if (ch != '?' && !equals(ch, strArr[strIdxStart + i + j], isCaseSensitive)) {
- continue strLoop;
- }
- }
-
- foundIdx = strIdxStart + i;
- break;
- }
-
- if (foundIdx == -1) {
- return false;
- }
-
- patIdxStart = patIdxTmp;
- strIdxStart = foundIdx + patLength;
- }
-
- // All characters in the string are used. Check if only '*'s are left
- // in the pattern. If so, we succeeded. Otherwise failure.
- for (int i = patIdxStart; i <= patIdxEnd; i++) {
- if (patArr[i] != '*') {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Tests whether two characters are equal.
- * @param c1 1st character
- * @param c2 2nd character
- * @param isCaseSensitive Whether to compare case sensitive
- * @return {@code true} if equal characters
- */
- public static boolean equals(char c1, char c2, boolean isCaseSensitive) {
- if (c1 == c2) {
- return true;
- }
- if (!isCaseSensitive) {
- // NOTE: Try both upper case and lower case as done by String.equalsIgnoreCase()
- if (Character.toUpperCase(c1) == Character.toUpperCase(c2)
- || Character.toLowerCase(c1) == Character.toLowerCase(c2)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Breaks a path up into a Vector of path elements, tokenizing on
- * <code>File.separator</code>.
- *
- * @param path Path to tokenize. Must not be {@code null}.
- * @return a List of path elements from the tokenized path
- */
- public static List<String> tokenizePath(String path) {
- return tokenizePath(path, File.separator);
- }
-
- public static List<String> tokenizePath(String path, String separator) {
- List<String> ret = new ArrayList<>();
- StringTokenizer st = new StringTokenizer(path, separator);
- while (st.hasMoreTokens()) {
- ret.add(st.nextToken());
- }
- return ret;
- }
-
- /** /**
- * Converts a path to one matching the target file system by applying the
- * "slashification" rules, converting it to a local path and
- * then translating its separator to the target file system one (if different
- * than local one)
- * @param path The input path
- * @param pathSeparator The separator used to build the input path
- * @param fs The target {@link FileSystem} - may not be {@code null}
- * @return The transformed path
- * @see #translateToLocalFileSystemPath(String, char, String)
- */
- public static String translateToLocalFileSystemPath(String path, char pathSeparator, FileSystem fs) {
- return translateToLocalFileSystemPath(path, pathSeparator, Objects.requireNonNull(fs, "No target file system").getSeparator());
- }
-
- /**
- * Converts a path to one matching the target file system by applying the
- * "slashification" rules, converting it to a local path and
- * then translating its separator to the target file system one (if different
- * than local one)
- * @param path The input path
- * @param pathSeparator The separator used to build the input path
- * @param fsSeparator The target file system separator
- * @return The transformed path
- * @see #applySlashifyRules(String, char)
- * @see #translateToLocalPath(String)
- * @see #translateToFileSystemPath(String, String, String)
- */
- public static String translateToLocalFileSystemPath(String path, char pathSeparator, String fsSeparator) {
- // In case double slashes and other patterns are used
- String slashified = applySlashifyRules(path, pathSeparator);
- // In case we are running on Windows
- String localPath = translateToLocalPath(slashified);
- return translateToFileSystemPath(localPath, File.separator, fsSeparator);
- }
-
- /**
- * Applies the "slashification" rules as specified in
- * <A HREF="http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_266">Single Unix Specification version 3, section 3.266</A>
- * and <A HREF="http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11">section 4.11 - Pathname resolution</A>
- * @param path The original path - ignored if {@code null}/empty or does
- * not contain any slashes
- * @param sepChar The "slash" character
- * @return The effective path - may be same as input if no changes required
- */
- public static String applySlashifyRules(String path, char sepChar) {
- if (GenericUtils.isEmpty(path)) {
- return path;
- }
-
- int curPos = path.indexOf(sepChar);
- if (curPos < 0) {
- return path; // no slashes to handle
- }
-
- int lastPos = 0;
- StringBuilder sb = null;
- while (curPos < path.length()) {
- curPos++; // skip the 1st '/'
-
- /*
- * As per Single Unix Specification version 3, section 3.266:
- *
- * Multiple successive slashes are considered to be the
- * same as one slash
- */
- int nextPos = curPos;
- while ((nextPos < path.length()) && (path.charAt(nextPos) == sepChar)) {
- nextPos++;
- }
-
- /*
- * At this stage, nextPos is the first non-slash character after a
- * possibly 'seqLen' sequence of consecutive slashes.
- */
- int seqLen = nextPos - curPos;
- if (seqLen > 0) {
- if (sb == null) {
- sb = new StringBuilder(path.length() - seqLen);
- }
-
- if (lastPos < curPos) {
- String clrText = path.substring(lastPos, curPos);
- sb.append(clrText);
- }
-
- lastPos = nextPos;
- }
-
- if (nextPos >= path.length()) {
- break; // no more data
- }
-
- curPos = path.indexOf(sepChar, nextPos);
- if (curPos < nextPos) {
- break; // no more slashes
- }
- }
-
- // check if any leftovers for the modified path
- if (sb != null) {
- if (lastPos < path.length()) {
- String clrText = path.substring(lastPos);
- sb.append(clrText);
- }
-
- path = sb.toString();
- }
-
- /*
- * At this point we know for sure that 'path' contains only SINGLE
- * slashes. According to section 4.11 - Pathname resolution
- *
- * A pathname that contains at least one non-slash character
- * and that ends with one or more trailing slashes shall be
- * resolved as if a single dot character ( '.' ) were appended
- * to the pathname.
- */
- if ((path.length() > 1) && (path.charAt(path.length() - 1) == sepChar)) {
- return path + ".";
- } else {
- return path;
- }
- }
-
- /**
- * Converts a possibly '/' separated path to a local path. <B>Note:</B>
- * takes special care of Windows drive paths - e.g., {@code C:}
- * by converting them to "C:\"
- *
- * @param path The original path - ignored if {@code null}/empty
- * @return The local path
- */
- public static String translateToLocalPath(String path) {
- if (GenericUtils.isEmpty(path) || (File.separatorChar == '/')) {
- return path;
- }
-
- // This code is reached if we are running on Windows
- String localPath = path.replace('/', File.separatorChar);
- // check if '/c:' prefix
- if ((localPath.charAt(0) == File.separatorChar) && isWindowsDriveSpecified(localPath, 1, localPath.length() - 1)) {
- localPath = localPath.substring(1);
- }
- if (!isWindowsDriveSpecified(localPath)) {
- return localPath; // assume a relative path
- }
-
- /*
- * Here we know that we have at least a "C:" string - make sure it
- * is followed by the local file separator. Note: if all we have is
- * just the drive, we will create a "C:\" path since this is the
- * preferred Windows way to refer to root drives in the file system
- */
- if (localPath.length() == 2) {
- return localPath + File.separator; // all we have is "C:"
- } else if (localPath.charAt(2) != File.separatorChar) {
- // be nice and add the missing file separator - C:foo => C:\foo
- return localPath.substring(0, 2) + File.separator + localPath.substring(2);
- } else {
- return localPath;
- }
- }
-
- public static boolean isWindowsDriveSpecified(CharSequence cs) {
- return isWindowsDriveSpecified(cs, 0, GenericUtils.length(cs));
- }
-
- public static boolean isWindowsDriveSpecified(CharSequence cs, int offset, int len) {
- if ((len < 2) || (cs.charAt(offset + 1) != ':')) {
- return false;
- }
-
- char drive = cs.charAt(offset);
- return ((drive >= 'a') && (drive <= 'z')) || ((drive >= 'A') && (drive <= 'Z'));
- }
-
- /**
- * Converts a path containing a specific separator to one using the
- * specified file-system one
- * @param path The input path - ignored if {@code null}/empty
- * @param pathSeparator The separator used to build the input path - may not
- * be {@code null}/empty
- * @param fs The target {@link FileSystem} - may not be {@code null}
- * @return The path where the separator used to build it is replaced by
- * the file-system one (if different)
- * @see FileSystem#getSeparator()
- * @see #translateToFileSystemPath(String, String, String)
- */
- public static String translateToFileSystemPath(String path, String pathSeparator, FileSystem fs) {
- return translateToFileSystemPath(path, pathSeparator, Objects.requireNonNull(fs, "No target file system").getSeparator());
- }
-
- /**
- * Converts a path containing a specific separator to one using the
- * specified file-system one
- * @param path The input path - ignored if {@code null}/empty
- * @param pathSeparator The separator used to build the input path - may not
- * be {@code null}/empty
- * @param fsSeparator The target file system separator - may not be {@code null}/empty
- * @return The path where the separator used to build it is replaced by
- * the file-system one (if different)
- * @throws IllegalArgumentException if path or file-system separator are {@code null}/empty
- * or if the separators are different and the path contains the target
- * file-system separator as it would create an ambiguity
- */
- public static String translateToFileSystemPath(String path, String pathSeparator, String fsSeparator) {
- ValidateUtils.checkNotNullAndNotEmpty(pathSeparator, "Missing path separator");
- ValidateUtils.checkNotNullAndNotEmpty(fsSeparator, "Missing file-system separator");
-
- if (GenericUtils.isEmpty(path) || Objects.equals(pathSeparator, fsSeparator)) {
- return path;
- }
-
- // make sure path does not contain the target separator
- if (path.contains(fsSeparator)) {
- ValidateUtils.throwIllegalArgumentException("File system replacement may yield ambiguous result for %s with separator=%s", path, fsSeparator);
- }
-
- // check most likely case
- if ((pathSeparator.length() == 1) && (fsSeparator.length() == 1)) {
- return path.replace(pathSeparator.charAt(0), fsSeparator.charAt(0));
- } else {
- return path.replace(pathSeparator, fsSeparator);
- }
- }
-
- /**
- * Returns dependency information on these two files. If src has been
- * modified later than target, it returns true. If target doesn't exist,
- * it likewise returns true. Otherwise, target is newer than src and
- * is not out of date, thus the method returns false. It also returns
- * false if the src file doesn't even exist, since how could the
- * target then be out of date.
- *
- * @param src the original file
- * @param target the file being compared against
- * @param granularity the amount in seconds of slack we will give in
- * determining out of dateness
- * @return whether the target is out of date
- */
- public static boolean isOutOfDate(File src, File target, int granularity) {
- if (!src.exists()) {
- return false;
- }
- if (!target.exists()) {
- return true;
- }
- return (src.lastModified() - granularity) > target.lastModified();
- }
-
- /**
- * "Flattens" a string by removing all whitespace (space, tab, linefeed,
- * carriage return, and formfeed). This uses StringTokenizer and the
- * default set of tokens as documented in the single arguement constructor.
- *
- * @param input a String to remove all whitespace.
- * @return a String that has had all whitespace removed.
- */
- public static String removeWhitespace(String input) {
- StringBuilder result = new StringBuilder();
- if (input != null) {
- StringTokenizer st = new StringTokenizer(input);
- while (st.hasMoreTokens()) {
- result.append(st.nextToken());
- }
- }
- return result.toString();
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/SshdEventListener.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/SshdEventListener.java b/sshd-core/src/main/java/org/apache/sshd/common/util/SshdEventListener.java
deleted file mode 100644
index ea6a3dd..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/SshdEventListener.java
+++ /dev/null
@@ -1,44 +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.util;
-
-import java.lang.reflect.Proxy;
-import java.util.EventListener;
-import java.util.Objects;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface SshdEventListener extends EventListener {
-
- /**
- * Makes sure that the listener is neither {@code null} nor a proxy
- *
- * @param <L> Type of {@link SshdEventListener} being validation
- * @param listener The listener instance
- * @param prefix Prefix text to be prepended to validation failure messages
- * @return The validated instance
- */
- static <L extends SshdEventListener> L validateListener(L listener, String prefix) {
- Objects.requireNonNull(listener, prefix + ": no instance");
- ValidateUtils.checkTrue(!Proxy.isProxyClass(listener.getClass()), prefix + ": proxies N/A");
- return listener;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
deleted file mode 100644
index a55e5d6..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/ValidateUtils.java
+++ /dev/null
@@ -1,216 +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.util;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.function.Function;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public final class ValidateUtils {
- private ValidateUtils() {
- throw new UnsupportedOperationException("No instance");
- }
-
- public static <T> T checkNotNull(T t, String message) {
- checkTrue(t != null, message);
- return t;
- }
-
- public static <T> T checkNotNull(T t, String message, Object arg) {
- checkTrue(t != null, message, arg);
- return t;
- }
-
- public static <T> T checkNotNull(T t, String message, long value) {
- checkTrue(t != null, message, value);
- return t;
- }
-
- public static <T> T checkNotNull(T t, String message, Object... args) {
- checkTrue(t != null, message, args);
- return t;
- }
-
- public static String checkNotNullAndNotEmpty(String t, String message) {
- t = checkNotNull(t, message).trim();
- checkTrue(GenericUtils.length(t) > 0, message);
- return t;
- }
-
- public static String checkNotNullAndNotEmpty(String t, String message, Object arg) {
- t = checkNotNull(t, message, arg).trim();
- checkTrue(GenericUtils.length(t) > 0, message, arg);
- return t;
- }
-
- public static String checkNotNullAndNotEmpty(String t, String message, Object... args) {
- t = checkNotNull(t, message, args).trim();
- checkTrue(GenericUtils.length(t) > 0, message, args);
- return t;
- }
-
- public static <K, V, M extends Map<K, V>> M checkNotNullAndNotEmpty(M t, String message, Object... args) {
- t = checkNotNull(t, message, args);
- checkTrue(GenericUtils.size(t) > 0, message, args);
- return t;
- }
-
- public static <T, C extends Collection<T>> C checkNotNullAndNotEmpty(C t, String message, Object... args) {
- t = checkNotNull(t, message, args);
- checkTrue(GenericUtils.size(t) > 0, message, args);
- return t;
- }
-
- public static <T, C extends Iterable<T>> C checkNotNullAndNotEmpty(C t, String message, Object... args) {
- t = checkNotNull(t, message, args);
- checkTrue(GenericUtils.isNotEmpty(t), message, args);
- return t;
- }
-
- public static byte[] checkNotNullAndNotEmpty(byte[] a, String message) {
- a = checkNotNull(a, message);
- checkTrue(NumberUtils.length(a) > 0, message);
- return a;
- }
-
- public static byte[] checkNotNullAndNotEmpty(byte[] a, String message, Object... args) {
- a = checkNotNull(a, message, args);
- checkTrue(NumberUtils.length(a) > 0, message, args);
- return a;
- }
-
- public static char[] checkNotNullAndNotEmpty(char[] a, String message) {
- a = checkNotNull(a, message);
- checkTrue(GenericUtils.length(a) > 0, message);
- return a;
- }
-
- public static char[] checkNotNullAndNotEmpty(char[] a, String message, Object... args) {
- a = checkNotNull(a, message, args);
- checkTrue(GenericUtils.length(a) > 0, message, args);
- return a;
- }
-
- public static int[] checkNotNullAndNotEmpty(int[] a, String message) {
- a = checkNotNull(a, message);
- checkTrue(NumberUtils.length(a) > 0, message);
- return a;
- }
-
- public static int[] checkNotNullAndNotEmpty(int[] a, String message, Object... args) {
- a = checkNotNull(a, message, args);
- checkTrue(NumberUtils.length(a) > 0, message, args);
- return a;
- }
-
- public static <T> T[] checkNotNullAndNotEmpty(T[] t, String message, Object... args) {
- t = checkNotNull(t, message, args);
- checkTrue(GenericUtils.length(t) > 0, message, args);
- return t;
- }
-
- public static <T> T checkInstanceOf(Object v, Class<T> expected, String message, long value) {
- Class<?> actual = checkNotNull(v, message, value).getClass();
- checkTrue(expected.isAssignableFrom(actual), message, value);
- return expected.cast(v);
- }
-
- public static <T> T checkInstanceOf(Object v, Class<T> expected, String message) {
- return checkInstanceOf(v, expected, message, GenericUtils.EMPTY_OBJECT_ARRAY);
- }
-
- public static <T> T checkInstanceOf(Object v, Class<T> expected, String message, Object arg) {
- Class<?> actual = checkNotNull(v, message, arg).getClass();
- checkTrue(expected.isAssignableFrom(actual), message, arg);
- return expected.cast(v);
- }
-
- public static <T> T checkInstanceOf(Object v, Class<T> expected, String message, Object... args) {
- Class<?> actual = checkNotNull(v, message, args).getClass();
- checkTrue(expected.isAssignableFrom(actual), message, args);
- return expected.cast(v);
- }
-
- public static void checkTrue(boolean flag, String message) {
- if (!flag) {
- throwIllegalArgumentException(message, GenericUtils.EMPTY_OBJECT_ARRAY);
- }
- }
-
- public static void checkTrue(boolean flag, String message, long value) {
- if (!flag) {
- throwIllegalArgumentException(message, value);
- }
- }
-
- public static void checkTrue(boolean flag, String message, Object arg) {
- if (!flag) {
- throwIllegalArgumentException(message, arg);
- }
- }
-
- public static void checkTrue(boolean flag, String message, Object... args) {
- if (!flag) {
- throwIllegalArgumentException(message, args);
- }
- }
-
- public static void throwIllegalArgumentException(String format, Object... args) {
- throw createFormattedException(IllegalArgumentException::new, format, args);
- }
-
- public static void checkState(boolean flag, String message) {
- if (!flag) {
- throwIllegalStateException(message, GenericUtils.EMPTY_OBJECT_ARRAY);
- }
- }
-
- public static void checkState(boolean flag, String message, long value) {
- if (!flag) {
- throwIllegalStateException(message, value);
- }
- }
-
- public static void checkState(boolean flag, String message, Object arg) {
- if (!flag) {
- throwIllegalStateException(message, arg);
- }
- }
-
- public static void checkState(boolean flag, String message, Object... args) {
- if (!flag) {
- throwIllegalStateException(message, args);
- }
- }
-
- public static void throwIllegalStateException(String format, Object... args) {
- throw createFormattedException(IllegalStateException::new, format, args);
- }
-
- public static <T extends Throwable> T createFormattedException(
- Function<? super String, ? extends T> constructor, String format, Object... args) {
- String message = String.format(format, args);
- return constructor.apply(message);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/VersionInfo.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/VersionInfo.java b/sshd-core/src/main/java/org/apache/sshd/common/util/VersionInfo.java
deleted file mode 100644
index ed1aab7..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/VersionInfo.java
+++ /dev/null
@@ -1,137 +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.util;
-
-import java.io.Serializable;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class VersionInfo implements Serializable, Comparable<VersionInfo> {
- private static final long serialVersionUID = -9127482432228413836L;
-
- private final int majorVersion;
- private final int minorVersion;
- private final int release;
- private final int buildNumber;
-
- public VersionInfo(int major, int minor) {
- this(major, minor, 0, 0);
- }
-
- public VersionInfo(int major, int minor, int release, int build) {
- this.majorVersion = major;
- this.minorVersion = minor;
- this.release = release;
- this.buildNumber = build;
- }
-
- public final int getMajorVersion() {
- return majorVersion;
- }
-
- public final int getMinorVersion() {
- return minorVersion;
- }
-
- public final int getRelease() {
- return release;
- }
-
- public final int getBuildNumber() {
- return buildNumber;
- }
-
- @Override
- public int hashCode() {
- return NumberUtils.hashCode(getMajorVersion(), getMinorVersion(), getRelease(), getBuildNumber());
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (this == obj) {
- return true;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- return compareTo((VersionInfo) obj) == 0;
- }
-
- @Override
- public int compareTo(VersionInfo o) {
- if (o == null) {
- return -1; // push nulls to end
- }
- if (o == this) {
- return 0;
- }
-
- int nRes = Integer.compare(getMajorVersion(), o.getMajorVersion());
- if (nRes == 0) {
- nRes = Integer.compare(getMinorVersion(), o.getMinorVersion());
- }
- if (nRes == 0) {
- nRes = Integer.compare(getRelease(), o.getRelease());
- }
- if (nRes == 0) {
- nRes = Integer.compare(getBuildNumber(), o.getBuildNumber());
- }
-
- return nRes;
- }
-
- @Override
- public String toString() {
- return NumberUtils.join('.', getMajorVersion(), getMinorVersion(), getRelease(), getBuildNumber());
- }
-
- /**
- * Parses a version string - assumed to contain at most 4 non-negative
- * components separated by a '.'. If less than 4 components are found, then
- * the rest are assumed to be zero. If more than 4 components found, then
- * only the 1st ones are parsed.
- *
- * @param version The version string - ignored if {@code null}/empty
- * @return The parsed {@link VersionInfo} - or {@code null} if empty input
- * @throws NumberFormatException If failed to parse any of the components
- * @throws IllegalArgumentException If any of the parsed components is negative
- */
- public static VersionInfo parse(String version) throws NumberFormatException {
- String[] comps = GenericUtils.split(version, '.');
- if (GenericUtils.isEmpty(comps)) {
- return null;
- }
-
- int[] values = new int[4];
- int maxValues = Math.min(comps.length, values.length);
- for (int index = 0; index < maxValues; index++) {
- String c = comps[index];
- int v = Integer.parseInt(c);
- ValidateUtils.checkTrue(v >= 0, "Invalid version component in %s", version);
- values[index] = v;
- }
-
- return new VersionInfo(values[0], values[1], values[2], values[3]);
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
deleted file mode 100644
index dd61473..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
+++ /dev/null
@@ -1,798 +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.util.buffer;
-
-import java.math.BigInteger;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPrivateCrtKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.DSAPrivateKeySpec;
-import java.security.spec.DSAPublicKeySpec;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPrivateKeySpec;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.RSAPrivateCrtKeySpec;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Objects;
-import java.util.function.IntUnaryOperator;
-import java.util.logging.Level;
-
-import org.apache.sshd.common.PropertyResolver;
-import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.NumberUtils;
-import org.apache.sshd.common.util.Readable;
-import org.apache.sshd.common.util.buffer.keys.BufferPublicKeyParser;
-import org.apache.sshd.common.util.logging.SimplifiedLog;
-import org.apache.sshd.common.util.security.SecurityUtils;
-
-/**
- * Provides an abstract message buffer for encoding SSH messages
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public abstract class Buffer implements Readable {
- protected final byte[] workBuf = new byte[Long.BYTES];
-
- protected Buffer() {
- super();
- }
-
- public abstract int rpos();
-
- public abstract void rpos(int rpos);
-
- public abstract int wpos();
-
- public abstract void wpos(int wpos);
-
- public abstract int capacity();
-
- public abstract byte[] array();
-
- public abstract void compact();
-
- public byte[] getCompactData() {
- int l = available();
- if (l > 0) {
- byte[] b = new byte[l];
- System.arraycopy(array(), rpos(), b, 0, l);
- return b;
- } else {
- return GenericUtils.EMPTY_BYTE_ARRAY;
- }
- }
-
- public void clear() {
- clear(true);
- }
-
- public abstract void clear(boolean wipeData);
-
- public boolean isValidMessageStructure(Class<?>... fieldTypes) {
- return isValidMessageStructure(GenericUtils.isEmpty(fieldTypes) ? Collections.emptyList() : Arrays.asList(fieldTypes));
- }
-
- public boolean isValidMessageStructure(Collection<Class<?>> fieldTypes) {
- if (GenericUtils.isEmpty(fieldTypes)) {
- return true;
- }
-
- int remainLen = available();
- int readOffset = 0;
- for (Class<?> ft : fieldTypes) {
- if ((ft == boolean.class) || (ft == Boolean.class)
- || (ft == byte.class) || (ft == Byte.class)) {
- if (remainLen < Byte.BYTES) {
- return false;
- }
-
- remainLen -= Byte.BYTES;
- readOffset += Byte.BYTES;
- } else if ((ft == short.class) || (ft == Short.class)) {
- if (remainLen < Short.BYTES) {
- return false;
- }
-
- remainLen -= Short.BYTES;
- readOffset += Short.BYTES;
- } else if ((ft == int.class) || (ft == Integer.class)) {
- if (remainLen < Integer.BYTES) {
- return false;
- }
-
- remainLen -= Integer.BYTES;
- readOffset += Integer.BYTES;
- } else if ((ft == long.class) || (ft == Long.class)) {
- if (remainLen < Long.BYTES) {
- return false;
- }
-
- remainLen -= Long.BYTES;
- readOffset += Long.BYTES;
- } else if ((ft == byte[].class) || (ft == String.class)) {
- if (remainLen < Integer.BYTES) {
- return false;
- }
-
- copyRawBytes(readOffset, workBuf, 0, Integer.BYTES);
- remainLen -= Integer.BYTES;
- readOffset += Integer.BYTES;
-
- long length = BufferUtils.getUInt(workBuf, 0, Integer.BYTES);
- if (length > remainLen) {
- return false;
- }
-
- remainLen -= (int) length;
- readOffset += (int) length;
- }
- }
-
- return true;
- }
-
- protected abstract void copyRawBytes(int offset, byte[] buf, int pos, int len);
-
- public String toHex() {
- return BufferUtils.toHex(array(), rpos(), available());
- }
-
- public void dumpHex(SimplifiedLog logger, String prefix, PropertyResolver resolver) {
- dumpHex(logger, BufferUtils.DEFAULT_HEXDUMP_LEVEL, prefix, resolver);
- }
-
- public void dumpHex(SimplifiedLog logger, Level level, String prefix, PropertyResolver resolver) {
- BufferUtils.dumpHex(logger, level, prefix, resolver, BufferUtils.DEFAULT_HEX_SEPARATOR, array(), rpos(), available());
- }
-
- /*======================
- Read methods
- ======================*/
-
- public int getUByte() {
- return getByte() & 0xFF;
- }
-
- public byte getByte() {
- ensureAvailable(Byte.BYTES);
- getRawBytes(workBuf, 0, Byte.BYTES);
- return workBuf[0];
- }
-
- public short getShort() {
- ensureAvailable(Short.BYTES);
- getRawBytes(workBuf, 0, Short.BYTES);
- short v = (short) ((workBuf[1] << Byte.SIZE) & 0xFF00);
- v |= (short) (workBuf[0] & 0xF);
- return v;
- }
-
- public int getInt() {
- return (int) getUInt();
- }
-
- public long getUInt() {
- ensureAvailable(Integer.BYTES);
- getRawBytes(workBuf, 0, Integer.BYTES);
- return BufferUtils.getUInt(workBuf, 0, Integer.BYTES);
- }
-
- public long getLong() {
- ensureAvailable(Long.BYTES);
- getRawBytes(workBuf, 0, Long.BYTES);
- long l = ((long) workBuf[0] << 56) & 0xff00000000000000L;
- l |= ((long) workBuf[1] << 48) & 0x00ff000000000000L;
- l |= ((long) workBuf[2] << 40) & 0x0000ff0000000000L;
- l |= ((long) workBuf[3] << 32) & 0x000000ff00000000L;
- l |= ((long) workBuf[4] << 24) & 0x00000000ff000000L;
- l |= ((long) workBuf[5] << 16) & 0x0000000000ff0000L;
- l |= ((long) workBuf[6] << 8) & 0x000000000000ff00L;
- l |= (workBuf[7]) & 0x00000000000000ffL;
- return l;
- }
-
- @SuppressWarnings("PMD.BooleanGetMethodName")
- public boolean getBoolean() {
- return getByte() != 0;
- }
-
- public String getString() {
- return getString(StandardCharsets.UTF_8);
- }
-
- /**
- * @param usePrependedLength If {@code true} then there is a 32-bit
- * value indicating the number of strings to read. Otherwise, the
- * method will use a "greedy" reading of strings while more
- * data available
- * @return A {@link Collection} of the read strings
- * @see #getStringList(boolean, Charset)
- */
- public Collection<String> getStringList(boolean usePrependedLength) {
- return getStringList(usePrependedLength, StandardCharsets.UTF_8);
- }
-
- /**
- * @param usePrependedLength If {@code true} then there is a 32-bit
- * value indicating the number of strings to read. Otherwise, the
- * method will use a "greedy" reading of strings while more
- * data available
- * @param charset The {@link Charset} to use for the string
- * @return A {@link Collection} of the read strings
- * @see #getStringList(int, Charset)
- * @see #getAvailableStrings()
- */
- public Collection<String> getStringList(boolean usePrependedLength, Charset charset) {
- if (usePrependedLength) {
- int count = getInt();
- return getStringList(count, charset);
- } else {
- return getAvailableStrings(charset);
- }
- }
-
- /**
- * @return The remaining data as a list of strings
- * @see #getAvailableStrings(Charset)
- */
- public Collection<String> getAvailableStrings() {
- return getAvailableStrings(StandardCharsets.UTF_8);
- }
-
- /**
- * @param charset The {@link Charset} to use for the strings
- * @return The remaining data as a list of strings
- * @see #available()
- * @see #getString(Charset)
- */
- public Collection<String> getAvailableStrings(Charset charset) {
- Collection<String> list = new LinkedList<>();
- while (available() > 0) {
- String s = getString(charset);
- list.add(s);
- }
-
- return list;
- }
-
- /**
- * @param count The <U>exact</U> number of strings to read - can be zero
- * @return A {@link List} with the specified number of strings
- * @see #getStringList(int, Charset)
- */
- public List<String> getStringList(int count) {
- return getStringList(count, StandardCharsets.UTF_8);
- }
-
- /**
- * @param count The <U>exact</U> number of strings to read - can be zero
- * @param charset The {@link Charset} of the strings
- * @return A {@link List} with the specified number of strings
- * @see #getString(Charset)
- */
- public List<String> getStringList(int count, Charset charset) {
- if (count == 0) {
- return Collections.emptyList();
- }
-
- List<String> list = new ArrayList<>(count);
- for (int index = 0; index < count; index++) {
- String s = getString(charset);
- list.add(s);
- }
-
- return list;
- }
-
- public abstract String getString(Charset charset);
-
- public BigInteger getMPInt() {
- return new BigInteger(getMPIntAsBytes());
- }
-
- public byte[] getMPIntAsBytes() {
- return getBytes();
- }
-
- public byte[] getBytes() {
- int len = getInt();
- if (len < 0) {
- throw new BufferException("Bad item length: " + len);
- }
- ensureAvailable(len);
- byte[] b = new byte[len];
- getRawBytes(b);
- return b;
- }
-
- public void getRawBytes(byte[] buf) {
- getRawBytes(buf, 0, buf.length);
- }
-
- public PublicKey getPublicKey() throws SshException {
- return getPublicKey(BufferPublicKeyParser.DEFAULT);
- }
-
- /**
- * @param parser A {@link BufferPublicKeyParser} to extract the key from the buffer
- * - never {@code null}
- * @return The extracted {@link PublicKey} - may be {@code null} if the parser so decided
- * @throws SshException If failed to extract the key
- * @see #getRawPublicKey(BufferPublicKeyParser)
- */
- public PublicKey getPublicKey(BufferPublicKeyParser<? extends PublicKey> parser) throws SshException {
- int ow = wpos();
- int len = getInt();
- wpos(rpos() + len);
- try {
- return getRawPublicKey(parser);
- } finally {
- wpos(ow);
- }
- }
-
- public PublicKey getRawPublicKey() throws SshException {
- return getRawPublicKey(BufferPublicKeyParser.DEFAULT);
- }
-
- /**
- * @param parser A {@link BufferPublicKeyParser} to extract the key from the buffer
- * - never {@code null}
- * @return The extracted {@link PublicKey} - may be {@code null} if the parser so decided
- * @throws SshException If failed to extract the key
- */
- public PublicKey getRawPublicKey(BufferPublicKeyParser<? extends PublicKey> parser) throws SshException {
- Objects.requireNonNull(parser, "No key data parser");
- try {
- String keyType = getString();
- if (!parser.isKeyTypeSupported(keyType)) {
- throw new NoSuchAlgorithmException("Key type=" + keyType + ") not supported by parser=" + parser);
- }
-
- return parser.getRawPublicKey(keyType, this);
- } catch (GeneralSecurityException e) {
- throw new SshException(e);
- }
- }
-
- public KeyPair getKeyPair() throws SshException {
- try {
- final PublicKey pub;
- final PrivateKey prv;
- final String keyAlg = getString();
- if (KeyPairProvider.SSH_RSA.equals(keyAlg)) {
- BigInteger e = getMPInt();
- BigInteger n = getMPInt();
- BigInteger d = getMPInt();
- BigInteger qInv = getMPInt();
- BigInteger q = getMPInt();
- BigInteger p = getMPInt();
- BigInteger dP = d.remainder(p.subtract(BigInteger.valueOf(1)));
- BigInteger dQ = d.remainder(q.subtract(BigInteger.valueOf(1)));
- KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
- pub = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
- prv = keyFactory.generatePrivate(new RSAPrivateCrtKeySpec(n, e, d, p, q, dP, dQ, qInv));
- } else if (KeyPairProvider.SSH_DSS.equals(keyAlg)) {
- BigInteger p = getMPInt();
- BigInteger q = getMPInt();
- BigInteger g = getMPInt();
- BigInteger y = getMPInt();
- BigInteger x = getMPInt();
- KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
- pub = keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
- prv = keyFactory.generatePrivate(new DSAPrivateKeySpec(x, p, q, g));
- } else if (KeyPairProvider.SSH_ED25519.equals(keyAlg)) {
- return SecurityUtils.extractEDDSAKeyPair(this, keyAlg);
- } else {
- ECCurves curve = ECCurves.fromKeyType(keyAlg);
- if (curve == null) {
- throw new NoSuchAlgorithmException("Unsupported key pair algorithm: " + keyAlg);
- }
- String curveName = curve.getName();
- ECParameterSpec params = curve.getParameters();
- return extractEC(curveName, params);
- }
-
- return new KeyPair(pub, prv);
- } catch (GeneralSecurityException e) {
- throw new SshException(e);
- }
- }
-
- protected KeyPair extractEC(String expectedCurveName, ECParameterSpec spec) throws GeneralSecurityException {
- String curveName = getString();
- if (!expectedCurveName.equals(curveName)) {
- throw new InvalidKeySpecException("extractEC(" + expectedCurveName + ") mismatched curve name: " + curveName);
- }
-
- byte[] groupBytes = getBytes();
- BigInteger exponent = getMPInt();
-
- if (spec == null) {
- throw new InvalidKeySpecException("extractEC(" + expectedCurveName + ") missing parameters for curve");
- }
-
- ECPoint group;
- try {
- group = ECCurves.octetStringToEcPoint(groupBytes);
- } catch (RuntimeException e) {
- throw new InvalidKeySpecException("extractEC(" + expectedCurveName + ")"
- + " failed (" + e.getClass().getSimpleName() + ")"
- + " to decode EC group for curve: " + e.getMessage(),
- e);
- }
-
- KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM);
- PublicKey pubKey = keyFactory.generatePublic(new ECPublicKeySpec(group, spec));
- PrivateKey privKey = keyFactory.generatePrivate(new ECPrivateKeySpec(exponent, spec));
- return new KeyPair(pubKey, privKey);
- }
-
- public void ensureAvailable(int reqLen) throws BufferException {
- int availLen = available();
- if (availLen < reqLen) {
- throw new BufferException("Underflow: requested=" + reqLen + ", available=" + availLen);
- }
- }
-
- /*======================
- Write methods
- ======================*/
-
- public void putByte(byte b) {
- ensureCapacity(Byte.BYTES);
- workBuf[0] = b;
- putRawBytes(workBuf, 0, Byte.BYTES);
- }
-
- public void putBuffer(Readable buffer) {
- putBuffer(buffer, true);
- }
-
- public abstract int putBuffer(Readable buffer, boolean expand);
-
- public abstract void putBuffer(ByteBuffer buffer);
-
- /**
- * Writes 16 bits
- *
- * @param i The 16-bit value
- */
- public void putShort(int i) {
- ensureCapacity(Short.BYTES);
- workBuf[0] = (byte) (i >> 8);
- workBuf[1] = (byte) i;
- putRawBytes(workBuf, 0, Short.BYTES);
- }
-
- /**
- * Writes 32 bits
- *
- * @param i The 32-bit value
- */
- public void putInt(long i) {
- BufferUtils.validateInt32Value(i, "Invalid 32-bit value: %d");
- ensureCapacity(Integer.BYTES);
- BufferUtils.putUInt(i, workBuf, 0, Integer.BYTES);
- putRawBytes(workBuf, 0, Integer.BYTES);
- }
-
- /**
- * Writes 64 bits
- *
- * @param i The 64-bit value
- */
- public void putLong(long i) {
- ensureCapacity(Long.BYTES);
- workBuf[0] = (byte) (i >> 56);
- workBuf[1] = (byte) (i >> 48);
- workBuf[2] = (byte) (i >> 40);
- workBuf[3] = (byte) (i >> 32);
- workBuf[4] = (byte) (i >> 24);
- workBuf[5] = (byte) (i >> 16);
- workBuf[6] = (byte) (i >> 8);
- workBuf[7] = (byte) i;
- putRawBytes(workBuf, 0, Long.BYTES);
- }
-
- public void putBoolean(boolean b) {
- putByte(b ? (byte) 1 : (byte) 0);
- }
-
- /**
- * Adds the bytes to the buffer and wipes the data from the
- * input buffer <U>after</U> having added it - useful for sensitive
- * information such as password
- *
- * @param b The buffer to add - OK if {@code null}
- */
- public void putAndWipeBytes(byte[] b) {
- putAndWipeBytes(b, 0, NumberUtils.length(b));
- }
-
- public void putAndWipeBytes(byte[] b, int off, int len) {
- putBytes(b, off, len);
-
- for (int pos = off, index = 0; index < len; pos++, index++) {
- b[pos] = (byte) 0;
- }
- }
-
- public void putBytes(byte[] b) {
- putBytes(b, 0, NumberUtils.length(b));
- }
-
- public void putBytes(byte[] b, int off, int len) {
- putInt(len);
- putRawBytes(b, off, len);
- }
-
- /**
- * Encodes the {@link Objects#toString(Object, String) toString} value of each member.
- *
- * @param objects The objects to be encoded in the buffer - OK if
- * {@code null}/empty
- * @param prependLength If {@code true} then the list is preceded by
- * a 32-bit count of the number of members in the list
- * @see #putStringList(Collection, Charset, boolean)
- */
- public void putStringList(Collection<?> objects, boolean prependLength) {
- putStringList(objects, StandardCharsets.UTF_8, prependLength);
- }
-
- /**
- * Encodes the {@link Objects#toString(Object, String) toString} value of each member
- *
- * @param objects The objects to be encoded in the buffer - OK if
- * {@code null}/empty
- * @param charset The {@link Charset} to use for encoding
- * @param prependLength If {@code true} then the list is preceded by
- * a 32-bit count of the number of members in the list
- * @see #putString(String, Charset)
- */
- public void putStringList(Collection<?> objects, Charset charset, boolean prependLength) {
- int numObjects = GenericUtils.size(objects);
- if (prependLength) {
- putInt(numObjects);
- }
-
- if (numObjects <= 0) {
- return;
- }
-
- objects.forEach(o -> putString(Objects.toString(o, null), charset));
- }
-
- public void putString(String string) {
- putString(string, StandardCharsets.UTF_8);
- }
-
- public void putString(String string, Charset charset) {
- if (GenericUtils.isEmpty(string)) {
- putBytes(GenericUtils.EMPTY_BYTE_ARRAY);
- } else {
- putBytes(string.getBytes(charset));
- }
- }
-
- /**
- * Zeroes the input array <U>after</U> having put the characters in the
- * buffer - useful for sensitive information such as passwords
- *
- * @param chars The characters to put in the buffer - may be {@code null}/empty
- * @see #putAndWipeChars(char[], Charset)
- * @see #putChars(char[], Charset)
- */
- public void putAndWipeChars(char[] chars) {
- putAndWipeChars(chars, 0, GenericUtils.length(chars));
- }
-
- public void putAndWipeChars(char[] chars, int offset, int len) {
- putAndWipeChars(chars, offset, len, StandardCharsets.UTF_8);
- }
-
- public void putAndWipeChars(char[] chars, Charset charset) {
- putAndWipeChars(chars, 0, GenericUtils.length(chars), charset);
- }
-
- public void putAndWipeChars(char[] chars, int offset, int len, Charset charset) {
- putChars(chars, offset, len, charset);
- for (int pos = offset, index = 0; index < len; index++, pos++) {
- chars[pos] = '\0';
- }
- }
-
- public void putChars(char[] chars) {
- putChars(chars, 0, GenericUtils.length(chars));
- }
-
- public void putChars(char[] chars, int offset, int len) {
- putChars(chars, offset, len, StandardCharsets.UTF_8);
- }
-
- public void putChars(char[] chars, Charset charset) {
- putChars(chars, 0, GenericUtils.length(chars), charset);
- }
-
- public void putChars(char[] chars, int offset, int len, Charset charset) {
- if (len <= 0) {
- putBytes(GenericUtils.EMPTY_BYTE_ARRAY);
- } else {
- putBuffer(charset.encode(CharBuffer.wrap(chars, offset, len)));
- }
- }
-
- public void putMPInt(BigInteger bi) {
- putMPInt(bi.toByteArray());
- }
-
- public void putMPInt(byte[] foo) {
- if ((foo[0] & 0x80) != 0) {
- putInt(foo.length + 1 /* padding */);
- putByte((byte) 0);
- } else {
- putInt(foo.length);
- }
- putRawBytes(foo);
- }
-
- public void putRawBytes(byte[] d) {
- putRawBytes(d, 0, d.length);
- }
-
- public abstract void putRawBytes(byte[] d, int off, int len);
-
- public void putPublicKey(PublicKey key) {
- int ow = wpos();
- putInt(0);
- int ow1 = wpos();
- putRawPublicKey(key);
- int ow2 = wpos();
- wpos(ow);
- putInt(ow2 - ow1);
- wpos(ow2);
- }
-
- public void putRawPublicKey(PublicKey key) {
- Objects.requireNonNull(key, "No key");
- if (key instanceof RSAPublicKey) {
- RSAPublicKey rsaPub = (RSAPublicKey) key;
-
- putString(KeyPairProvider.SSH_RSA);
- putMPInt(rsaPub.getPublicExponent());
- putMPInt(rsaPub.getModulus());
- } else if (key instanceof DSAPublicKey) {
- DSAPublicKey dsaPub = (DSAPublicKey) key;
- DSAParams dsaParams = dsaPub.getParams();
-
- putString(KeyPairProvider.SSH_DSS);
- putMPInt(dsaParams.getP());
- putMPInt(dsaParams.getQ());
- putMPInt(dsaParams.getG());
- putMPInt(dsaPub.getY());
- } else if (key instanceof ECPublicKey) {
- ECPublicKey ecKey = (ECPublicKey) key;
- ECParameterSpec ecParams = ecKey.getParams();
- ECCurves curve = ECCurves.fromCurveParameters(ecParams);
- if (curve == null) {
- throw new BufferException("Unsupported EC curve parameters");
- }
-
- putString(curve.getKeyType());
- putString(curve.getName());
- putBytes(ECCurves.encodeECPoint(ecKey.getW(), ecParams));
- } else if (SecurityUtils.EDDSA.equals(key.getAlgorithm())) {
- SecurityUtils.putRawEDDSAPublicKey(this, key);
- } else {
- throw new BufferException("Unsupported raw public key algorithm: " + key.getAlgorithm());
- }
- }
-
- public void putKeyPair(KeyPair kp) {
- PublicKey pubKey = kp.getPublic();
- PrivateKey prvKey = kp.getPrivate();
- if (prvKey instanceof RSAPrivateCrtKey) {
- RSAPublicKey rsaPub = (RSAPublicKey) pubKey;
- RSAPrivateCrtKey rsaPrv = (RSAPrivateCrtKey) prvKey;
-
- putString(KeyPairProvider.SSH_RSA);
- putMPInt(rsaPub.getPublicExponent());
- putMPInt(rsaPub.getModulus());
- putMPInt(rsaPrv.getPrivateExponent());
- putMPInt(rsaPrv.getCrtCoefficient());
- putMPInt(rsaPrv.getPrimeQ());
- putMPInt(rsaPrv.getPrimeP());
- } else if (pubKey instanceof DSAPublicKey) {
- DSAPublicKey dsaPub = (DSAPublicKey) pubKey;
- DSAParams dsaParams = dsaPub.getParams();
- DSAPrivateKey dsaPrv = (DSAPrivateKey) prvKey;
-
- putString(KeyPairProvider.SSH_DSS);
- putMPInt(dsaParams.getP());
- putMPInt(dsaParams.getQ());
- putMPInt(dsaParams.getG());
- putMPInt(dsaPub.getY());
- putMPInt(dsaPrv.getX());
- } else if (pubKey instanceof ECPublicKey) {
- ECPublicKey ecPub = (ECPublicKey) pubKey;
- ECPrivateKey ecPriv = (ECPrivateKey) prvKey;
- ECParameterSpec ecParams = ecPub.getParams();
- ECCurves curve = ECCurves.fromCurveParameters(ecParams);
- if (curve == null) {
- throw new BufferException("Unsupported EC curve parameters");
- }
-
- putString(curve.getKeyType());
- putString(curve.getName());
- putBytes(ECCurves.encodeECPoint(ecPub.getW(), ecParams));
- putMPInt(ecPriv.getS());
- } else if (SecurityUtils.EDDSA.equals(pubKey.getAlgorithm())) {
- SecurityUtils.putEDDSAKeyPair(this, pubKey, prvKey);
- } else {
- throw new BufferException("Unsupported key pair algorithm: " + pubKey.getAlgorithm());
- }
- }
-
- protected void ensureCapacity(int capacity) {
- ensureCapacity(capacity, BufferUtils.DEFAULT_BUFFER_GROWTH_FACTOR);
- }
-
- /**
- * @param capacity The requires capacity
- * @param growthFactor An {@link IntUnaryOperator} that is invoked
- * if the current capacity is insufficient. The argument is the minimum
- * required new data length, the function result should be the
- * effective new data length to be allocated - if less than minimum
- * then an exception is thrown
- */
- public abstract void ensureCapacity(int capacity, IntUnaryOperator growthFactor);
-
- protected abstract int size();
-
- @Override
- public String toString() {
- return "Buffer [rpos=" + rpos() + ", wpos=" + wpos() + ", size=" + size() + "]";
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferException.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferException.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferException.java
deleted file mode 100644
index 93f4e6b..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferException.java
+++ /dev/null
@@ -1,30 +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.util.buffer;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class BufferException extends RuntimeException {
- private static final long serialVersionUID = 658645233475011039L;
-
- public BufferException(String message) {
- super(message);
- }
-}
\ No newline at end of file
[28/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_ecdsa
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_ecdsa b/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_ecdsa
new file mode 100644
index 0000000..31b1268
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_ecdsa
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIPKmiQzAASg656IP4PuuElLdLdO/MIXrGxQG6tGkKZ1HoAoGCCqGSM49
+AwEHoUQDQgAEobHtw9wkL332ep9fi8Gw5g8sEGwslNonPUCDR6YUZ9mjOehliLpF
+DLHLxlIFafrVM+LIpagjpRKZcnpGPWQDnA==
+-----END EC PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_rsa
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_rsa b/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_rsa
new file mode 100644
index 0000000..afc6aa8
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/client/config/keys/id_rsa
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEoQIBAAKCAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2c
+tGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZB
+gIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlE
+aaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl
+5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwh
+V+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMwIBIwKCAQALW02YHN4OJz1Siypj
+xoNi87slUaBKBF/NlkWauGUIcpZFMTwnkIn6vCz5MhRbQC4oadRDzFNUrC/g7HdH
+prlqYe2P7uEGIfMb3YNFdk3tgOHmRsHqFgFMpVWsOjlTxNTUsQ74N3Isuxnha4wY
+9f90sBULc+WRdRvO9jbkSDaqoYVKAqCFWtocL+ZWwBXWrIrsQW4PElgZ/duc5DX7
+eeJ5DXCSj9dO+1KxsWEOKaoeABEegrRVys1/shcDNPhf/p0QShKIdPcpnDUc8cny
+1bq8GSt6jEQ+tuRoSnYrY+RD+mlkHrx373Xc1a9woV+QKTThmd9TQ8gzHMHNqq0a
+7kR7AoGBAOuPOTRiKaDtQyMTEX7eeHsPNE24EgvONjNpxyQ6gKGthG5SiB0IO7mP
+r7EggbR2EY8eMCY5HjvxzxgH86n2Pqbzsr6UlQq7YTPupCm/7fPgRknu917GA20f
+1cuY8B04Jp4FIGryBmCcScX6usXXhjfAvYCWWfkSytA8gX9+b1TNAoGBANf8shbp
+wRnQfgAzw2S+xs29pdwa6Jb/xuLvHSyklmgidrK4nsVI8G+zeCqwkqkNM02sM+vR
+c8EX7+myrGf+S2V3JS3AMNXEhavrWVH0CuqFHlBjSwHZ0uKuPpWHlCnud+23AdQz
+Bf1H7tYKt5es3J/B37o4YxhAL6U9qq+ewZH/AoGBAOTURjLjA94oT9jt870SoOyS
+bVLQEYfPol3UeE8UQnEsN4Ec+UDGK2PNaNfzsTL2WjNB5aF5UJIA184znD60seQC
+raMxQFORdF5ViYekgMEFwJ+XrnlSpD4e7PGqgtqOUWZOH33VKsRADSa5DTU3xDYo
+8porp9wDoHKD64MqXYWTAoGADFeVJeF4v6WDivukw+2k9dBSntz3WRXuG5iilNY0
+evqnsnDzITdeMkTFCcDycA9iBHA9ezCKRYxW2id3kOn1rgbO7KvliI7jESNkMJGa
+OUlvck7RFgxyc1pp+ep9fr0rbKtfMLJ1Xu4q56jXSn7oCSEFeFr+WSg9PKRwJ0rm
+fV8CgYAkYOiNY8jH5lwwoPWOIPJg62zdzmRQktrU3z7Nzk5udN6JnG3g57QjkozX
+AgHARKQ2MuXW9OfOnYNhbGeavcBQmg5XUx3eL4PRFw8mFZdjpBD/kM/dfCEwEta3
+FpRlVGn0RNqVV5xxClObD/CikkDqKZG4MSj3CrO3JK33gl1Lgg==
+-----END RSA PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair
new file mode 100644
index 0000000..217d508
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair
@@ -0,0 +1,21 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsQAAAAdzc2gtZH
+NzAAAAgQD3axy1MBdh4TJC+T22DKKDe7sXnDrgJRs1jBcDl/qPdQDoLlvvTd8oHggXkIzI
+6Tx2ldiJ3KADMkDg1sBSU5zenIr9wisfEQOLNqfIOOirR9Z8jULGVlDO+pPwps0P9bcOka
+JCAfABNth6Oaz3NJjfFD8ZmmWDVaN2rSi1J3mjnwAAABUA5be00MaSti06Hor265V8Ggtj
+i/0AAACAa9MU8kBJ4Z1+UsrWruVr72eckEwjOPcRFeA6MoSM70GGdYVkLyQ78MMjKAFZcb
+seYT3lsja+c4LQ7yErROHSGFo+ImQnYGY3Cos2mS8tRBHtZ4YmKsBmS3Zrb4lEfauA1pgD
+pcGrUAavniKIAB/C9cpvj+IpZ36+rHpap4JKJ7kAAACAQP4NRzjtauzrDJvs73c0DPczAk
+LAan+90ouvIuI0CmMxFrSqLIIEaX3cB/P4dkKtBsMh0CxwJpoXfn+TO+1j7i08GuMONeqB
+3lOHn1/MB2qUAZh/kdU8RUsxWkrG80JMeP1kew1sHaoRfOUZ8+Xw/RL7nNmhhmHJR3rkgR
+8EoWEAAAHoMgIkejICJHoAAAAHc3NoLWRzcwAAAIEA92sctTAXYeEyQvk9tgyig3u7F5w6
+4CUbNYwXA5f6j3UA6C5b703fKB4IF5CMyOk8dpXYidygAzJA4NbAUlOc3pyK/cIrHxEDiz
+anyDjoq0fWfI1CxlZQzvqT8KbND/W3DpGiQgHwATbYejms9zSY3xQ/GZplg1Wjdq0otSd5
+o58AAAAVAOW3tNDGkrYtOh6K9uuVfBoLY4v9AAAAgGvTFPJASeGdflLK1q7la+9nnJBMIz
+j3ERXgOjKEjO9BhnWFZC8kO/DDIygBWXG7HmE95bI2vnOC0O8hK0Th0hhaPiJkJ2BmNwqL
+NpkvLUQR7WeGJirAZkt2a2+JRH2rgNaYA6XBq1AGr54iiAAfwvXKb4/iKWd+vqx6WqeCSi
+e5AAAAgED+DUc47Wrs6wyb7O93NAz3MwJCwGp/vdKLryLiNApjMRa0qiyCBGl93Afz+HZC
+rQbDIdAscCaaF35/kzvtY+4tPBrjDjXqgd5Th59fzAdqlAGYf5HVPEVLMVpKxvNCTHj9ZH
+sNbB2qEXzlGfPl8P0S+5zZoYZhyUd65IEfBKFhAAAAFGL9BNpShiQnNZ1noW8Woxyq1GuH
+AAAADnJvb3RAdWJ1bnR1LTE1AQIDBAU=
+-----END OPENSSH PRIVATE KEY-----
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair.pub
new file mode 100644
index 0000000..c3feece
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-DSA-KeyPair.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAPdrHLUwF2HhMkL5PbYMooN7uxecOuAlGzWMFwOX+o91AOguW+9N3ygeCBeQjMjpPHaV2IncoAMyQODWwFJTnN6civ3CKx8RA4s2p8g46KtH1nyNQsZWUM76k/CmzQ/1tw6RokIB8AE22Ho5rPc0mN8UPxmaZYNVo3atKLUneaOfAAAAFQDlt7TQxpK2LToeivbrlXwaC2OL/QAAAIBr0xTyQEnhnX5Sytau5WvvZ5yQTCM49xEV4DoyhIzvQYZ1hWQvJDvwwyMoAVlxux5hPeWyNr5zgtDvIStE4dIYWj4iZCdgZjcKizaZLy1EEe1nhiYqwGZLdmtviUR9q4DWmAOlwatQBq+eIogAH8L1ym+P4ilnfr6selqngkonuQAAAIBA/g1HOO1q7OsMm+zvdzQM9zMCQsBqf73Si68i4jQKYzEWtKosggRpfdwH8/h2Qq0GwyHQLHAmmhd+f5M77WPuLTwa4w416oHeU4efX8wHapQBmH+R1TxFSzFaSsbzQkx4/WR7DWwdqhF85Rnz5fD9Evuc2aGGYclHeuSBHwShYQ== root@ubuntu-15
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair
new file mode 100644
index 0000000..403ff40
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair
@@ -0,0 +1,12 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS
+1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQAGRPv5eCmd3jFTCWrioWVHgQhHn/d
+ir8nriiEonZDPP+hEjX1AiYyahfvFWoqKI4lKRzoEmF5Wk6ct+9LM0JFGcEAck7Z3J/NXt
+CnHeEvnusHMoANjhKLExBURROOOTGziyHMuGBMBIgRFnf4rBhiTzduexJnaMglyqxIrDpG
+hOxwhQAAAAEQxPcsaMT3LGgAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ
+AAAIUEABkT7+Xgpnd4xUwlq4qFlR4EIR5/3Yq/J64ohKJ2Qzz/oRI19QImMmoX7xVqKiiO
+JSkc6BJheVpOnLfvSzNCRRnBAHJO2dyfzV7Qpx3hL57rBzKADY4SixMQVEUTjjkxs4shzL
+hgTASIERZ3+KwYYk83bnsSZ2jIJcqsSKw6RoTscIUAAAAAQQ+HCwVtvFlnRydGXZ+xpyKM
+KxDp5h7YMg5/dpRFrp3qNonm5/RHoT2Hw9i5GZtrXT2xPiR69wLOzTb4pnIWlENfAAAADn
+Jvb3RAdWJ1bnR1LTE1AQIDBAU=
+-----END OPENSSH PRIVATE KEY-----
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair.pub
new file mode 100644
index 0000000..bd43737
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ECDSA-KeyPair.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAAZE+/l4KZ3eMVMJauKhZUeBCEef92KvyeuKISidkM8/6ESNfUCJjJqF+8VaioojiUpHOgSYXlaTpy370szQkUZwQByTtncn81e0Kcd4S+e6wcygA2OEosTEFRFE445MbOLIcy4YEwEiBEWd/isGGJPN257EmdoyCXKrEisOkaE7HCFAA== root@ubuntu-15
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair
new file mode 100644
index 0000000..3176f3b
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACB0Tsaj0scs8fYKMlnz2Mncky545NoICP9eGMGIpo5G3QAAAJjCVtyJwlbc
+iQAAAAtzc2gtZWQyNTUxOQAAACB0Tsaj0scs8fYKMlnz2Mncky545NoICP9eGMGIpo5G3Q
+AAAEDjQpuV2OWHZVy7R09w6bw2DnBa1UdZrsAmQ7dPyxasx3ROxqPSxyzx9goyWfPYydyT
+Lnjk2ggI/14YwYimjkbdAAAADnJvb3RAdWJ1bnR1LTE1AQIDBAUGBw==
+-----END OPENSSH PRIVATE KEY-----
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair.pub
new file mode 100644
index 0000000..128e883
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-ED25519-KeyPair.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHROxqPSxyzx9goyWfPYydyTLnjk2ggI/14YwYimjkbd root@ubuntu-15
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair
new file mode 100644
index 0000000..84ee354
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair
@@ -0,0 +1,49 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
+NhAAAAAwEAAQAAAgEArsY72FUUnK8yLqaY3WkotvMFm2aryoVVPbaHiXMqmKTTQPBFshwe
+Rk+wEJBE7th4fIq2MuTu+hMxEQ8+oYJ47Ka1+F96QE1F0uu1Tuec/ZpeuEiXsTBApWZ5Sa
+AMqQ3gMflfqgp96ipV3SOpLyEG9GIqVMJYNE/ggwIV7Uc4ybC21Zy3I7QOfHho8BeaMjV/
+703rlu2UWs8Twjo7VMWLvnGMUjNMzNGql9WouLEzHshmkUZBRdKvnTjsT9vx1q5OycaH/g
+Hos23EbNzn50rbiIm93PcZ/otGBjjfbHcsRmjxZhqCzLss5Nr93PLIePN8Cez91S7aLjCw
+Ri4ugKxK0EZwNHnHPi0yR0eI1eMfJLttdYnrOZiSxEVbCsZiDfTybuBYUDaH2EtgmzH9N2
+5IZeJOjrjE68NZhiwXnaIoHvfpkPTkWMSGLsLY3Kg0dYwUbAy9ErqkS/MEMmN70OxIxqTI
+hFSZ0bv3FBbZTZnl/GAEWmV+KSjURKuJlS9EBYE4ZvzYhGi6K7g3IQJZwrcqy6s8u24r5u
+qAQCihfuWFabq4nSYB1zczQ7Qq+V8wemak6lgoFGq4f58k3FUhkJbwQRFJoQYvaMI63dls
+9q67bkocjycsGPvWDU7IE9dVs36z7FevlQjsoHwAr4+0T1PFsqHn++KJ0p3AlopIV7ObS0
+cAAAdIyEoveMhKL3gAAAAHc3NoLXJzYQAAAgEArsY72FUUnK8yLqaY3WkotvMFm2aryoVV
+PbaHiXMqmKTTQPBFshweRk+wEJBE7th4fIq2MuTu+hMxEQ8+oYJ47Ka1+F96QE1F0uu1Tu
+ec/ZpeuEiXsTBApWZ5SaAMqQ3gMflfqgp96ipV3SOpLyEG9GIqVMJYNE/ggwIV7Uc4ybC2
+1Zy3I7QOfHho8BeaMjV/703rlu2UWs8Twjo7VMWLvnGMUjNMzNGql9WouLEzHshmkUZBRd
+KvnTjsT9vx1q5OycaH/gHos23EbNzn50rbiIm93PcZ/otGBjjfbHcsRmjxZhqCzLss5Nr9
+3PLIePN8Cez91S7aLjCwRi4ugKxK0EZwNHnHPi0yR0eI1eMfJLttdYnrOZiSxEVbCsZiDf
+TybuBYUDaH2EtgmzH9N25IZeJOjrjE68NZhiwXnaIoHvfpkPTkWMSGLsLY3Kg0dYwUbAy9
+ErqkS/MEMmN70OxIxqTIhFSZ0bv3FBbZTZnl/GAEWmV+KSjURKuJlS9EBYE4ZvzYhGi6K7
+g3IQJZwrcqy6s8u24r5uqAQCihfuWFabq4nSYB1zczQ7Qq+V8wemak6lgoFGq4f58k3FUh
+kJbwQRFJoQYvaMI63dls9q67bkocjycsGPvWDU7IE9dVs36z7FevlQjsoHwAr4+0T1PFsq
+Hn++KJ0p3AlopIV7ObS0cAAAADAQABAAACAEC2ca42Qn2JeSFA2lUQ/NxQu4DA0VcZa2iA
+T0sbc2g1j2r6DDRYqPULyHs5j+yIBoTGr5PEpJ+/v2k7pcsTjkbBq5sdbxyj//iLAgUHSV
++1auD9L/2Rij4z5TDXrBhkZODcHwnM/LZZmpVZAOnMZtDSEC52D0a/VLfta8UBnAtB/VC2
+yDDWGI8J3oEXnCZ/HLG0vkiSC15cUkqWWSWYR5fqXzSIfiVQdMPnfqN0KSN6vE+KHd0Gl7
+O1QfqHy9HblJytf+kTN+BIAnT0joFbfFHf/ob0j5FqbDEdmcyFi8U+UjOmGUxI+eR7GYhX
+9NakAQeD0rC+ulZj/MDflKRcJw6+H/wHn2pdhahZY1A644SFin4SliFKl57njRkY/psjSg
+jpP0Nneg4+YFyFk9iUnmzxStJwuGE48I5eRESTFKgaMce5IVGOpz/YchQ9AL+ll2Qy6JfN
+nhpjuZD6Zh5KwsuLtCbaGfu8K8bbMEZg8F7jU6UdbpLmRLroKt6yFhffEp/tRrsX2hFoMt
+qCMpTEX2G+EzItPfFzq3/bFtnMxHmDiu4P8J03ALza/0OSIa4bdqMYG+97VBMbMeT6U76Q
+iYoDJH5/cdCBO/Cx+elM7oyFOFaV928h+6p01LGywcQdsgFPDyHSjXnF9t1fnLN4vhz5vU
+9QiVY7ldr+3PfxxpnRAAABAQDI87mazAnKsSJtkl70etg7vexltSxqkbhV/Hd5TYVMn19v
+U6rIjCY7q2xlHaUJdDNggwMpU679amkRP5BFPk9G4u8Xf5MyyDQpv+Hgpl7H0B1qEeJsBs
+huxURrX3EhLmb3300onEZXvlRwKOelIoVx5RZLUPhY0SbjBgH4sbO9NlYzs8xp4nLyK/jq
+DsqzmkkoQvK8zMybs3JwtGmTjDHvzmgcBgpm+s9gXyDJn3xe4CBW7v92N5/7eUehdXwomL
+4eDs/gR9QRb71bBSv9UaF5nK/TGJlysiM71JhBa2lmXlWg7KhALAu3SdTxxdvw0oyY+q6F
+E8zC0ljNMId8imVoAAABAQDi0m1WAW02b/IqwsJz0CAJXGpvhnhTSak3muZhTM0LPoavIe
+ow9KYL5Or4kQvbQXraYcaaRTp1Q9yJOrnXszAUWCGzqe38bLsI/SoRynLQaGEhdy1gr4tP
+WFKw0M34AshzyauEwHAVJIm6KZW/LgHsrxSXPFAABNQkQtB4olokNgSb/bysgo06OkkMBr
+N8mfY40m90RjTIvg3Cwei7xmna67MO1Bxzis8c7qQlf4hshFwnp+ixeLuhteYG1/DGxkfz
+yZ4UYXVKiuq3Xq+jFpYbKJeU17HL5TQFX34KCMN0BsMCcHLJ222IZkvFZj50715Z+jh/Vp
+xa6et++PX26BnVAAABAQDFQc26F12qnGA4heqa7Ig9WspGLEB4x5+cANOKaT/LtUFYWaYd
+O7Meeijkk+C1XcoOMac62QysRmjk8JEWdxgLiCFuKLp+6vxZLmEXBrBF0guHGv4/HlbpYn
+CxpMenxr0dhhwd2Flr1VtiadzEAfnc6MZE4byU8vVssYXORpVYIU46uI2OzIalnBhTXJYW
+KuamE/R+O1+dqnGgevQqUBkA5p4k8w+RMEY3hvSuNd6TFy5uoLXkCyOOpo60fGFVqMZyvJ
+IgU1et0umOA/JHrGzKHAl7WibRo4kf5MrPRLiDeDu92mqpwOl9NQjuTMR0OdZcXwOdrlG+
+daAgz5LetOKrAAAADnJvb3RAdWJ1bnR1LTE1AQIDBA==
+-----END OPENSSH PRIVATE KEY-----
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair.pub
new file mode 100644
index 0000000..c59321c
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParserTest-RSA-KeyPair.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCuxjvYVRScrzIuppjdaSi28wWbZqvKhVU9toeJcyqYpNNA8EWyHB5GT7AQkETu2Hh8irYy5O76EzERDz6hgnjsprX4X3pATUXS67VO55z9ml64SJexMEClZnlJoAypDeAx+V+qCn3qKlXdI6kvIQb0YipUwlg0T+CDAhXtRzjJsLbVnLcjtA58eGjwF5oyNX/vTeuW7ZRazxPCOjtUxYu+cYxSM0zM0aqX1ai4sTMeyGaRRkFF0q+dOOxP2/HWrk7Jxof+AeizbcRs3OfnStuIib3c9xn+i0YGON9sdyxGaPFmGoLMuyzk2v3c8sh483wJ7P3VLtouMLBGLi6ArErQRnA0ecc+LTJHR4jV4x8ku211ies5mJLERVsKxmIN9PJu4FhQNofYS2CbMf03bkhl4k6OuMTrw1mGLBedoige9+mQ9ORYxIYuwtjcqDR1jBRsDL0SuqRL8wQyY3vQ7EjGpMiEVJnRu/cUFtlNmeX8YARaZX4pKNREq4mVL0QFgThm/NiEaLoruDchAlnCtyrLqzy7bivm6oBAKKF+5YVpuridJgHXNzNDtCr5XzB6ZqTqWCgUarh/nyTcVSGQlvBBEUmhBi9owjrd2Wz2rrtuShyPJywY+9YNTsgT11WzfrPsV6+VCOygfACvj7RPU8Wyoef74onSncCWikhXs5tLRw== root@ubuntu-15
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair
new file mode 100644
index 0000000..59af379
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACB0Tsaj0scs8fYKMlnz2Mncky545NoICP9eGMGIpo5G3QAAAJjCVtyJwlbc
+iQAAAAtzc2gtZWQyNTUxOQAAACB0Tsaj0scs8fYKMlnz2Mncky545NoICP9eGMGIpo5G3Q
+AAAEDjQpuV2OWHZVy7R09w6bw2DnBa1UdZrsAmQ7dPyxasx3ROxqPSxyzx9goyWfPYydyT
+Lnjk2ggI/14YwYimjkbdAAAADnJvb3RAdWJ1bnR1LTE1AQIDBAUGBw==
+-----END OPENSSH PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub
new file mode 100644
index 0000000..128e883
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/EDDSAProviderTest-EDDSA-OpenSSH-KeyPair.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHROxqPSxyzx9goyWfPYydyTLnjk2ggI/14YwYimjkbd root@ubuntu-15
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-DSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-DSA-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-DSA-KeyPair
new file mode 100644
index 0000000..1d3ef24
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-DSA-KeyPair
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBvAIBAAKBgQDIPyMbBuQcZxeYDOyCqqkdK37cWQvp+RpWzvieB/oiG/ykfDQX
+oZMRtwqwWTBfejNitbBBmC6G/t5OK+9aFmj7pfJ+a7fZKXfiUquIg9soDsoOindf
+2AwR6MZ3os8uiP2xrC8IQAClnETa15mFShs4a4b2VjddgCQ6tphnY97MywIVAPtr
+YyW11RIXsVTf/9KlbhYaNlt5AoGAX9JzbHykC/0xDKOyKU6xDIOVdEZ0ooAl9Psl
+BEUuNhlv2XgmQScO6C9l2W7gbbut7zIw4FaZ2/dgXa3D4IyexBVug5XMnrssErZo
+NcoF5g0dgEAsb9Hl9gkIK3VHM5kWteeUg1VE700JTtSMisdL8CgIdR+xN8iVH5Ew
+CbLWxmECgYEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+ab
+YpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLa
+XWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7ACFQCE6flG
+nmVCAbzo9YsbdJWBnxMnBA==
+-----END DSA PRIVATE KEY-----
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-DSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-DSA-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-DSA-KeyPair.pub
new file mode 100644
index 0000000..c0790ed
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-DSA-KeyPair.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAMg/IxsG5BxnF5gM7IKqqR0rftxZC+n5GlbO+J4H+iIb/KR8NBehkxG3CrBZMF96M2K1sEGYLob+3k4r71oWaPul8n5rt9kpd+JSq4iD2ygOyg6Kd1/YDBHoxneizy6I/bGsLwhAAKWcRNrXmYVKGzhrhvZWN12AJDq2mGdj3szLAAAAFQD7a2MltdUSF7FU3//SpW4WGjZbeQAAAIBf0nNsfKQL/TEMo7IpTrEMg5V0RnSigCX0+yUERS42GW/ZeCZBJw7oL2XZbuBtu63vMjDgVpnb92BdrcPgjJ7EFW6DlcyeuywStmg1ygXmDR2AQCxv0eX2CQgrdUczmRa155SDVUTvTQlO1IyKx0vwKAh1H7E3yJUfkTAJstbGYQAAAIEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJVc64kFs4yx+abYpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4dVbQZsdt+SLaXWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3T7A= dsa-key-20130709
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-256-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-256-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-256-KeyPair
new file mode 100644
index 0000000..31b1268
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-256-KeyPair
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIPKmiQzAASg656IP4PuuElLdLdO/MIXrGxQG6tGkKZ1HoAoGCCqGSM49
+AwEHoUQDQgAEobHtw9wkL332ep9fi8Gw5g8sEGwslNonPUCDR6YUZ9mjOehliLpF
+DLHLxlIFafrVM+LIpagjpRKZcnpGPWQDnA==
+-----END EC PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-256-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-256-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-256-KeyPair.pub
new file mode 100644
index 0000000..1c9763f
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-256-KeyPair.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKGx7cPcJC999nqfX4vBsOYPLBBsLJTaJz1Ag0emFGfZoznoZYi6RQyxy8ZSBWn61TPiyKWoI6USmXJ6Rj1kA5w= root@osv-linux
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-384-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-384-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-384-KeyPair
new file mode 100644
index 0000000..29af76f
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-384-KeyPair
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDB15z4n/vjug4fcEXPcgeonCHQuxJOwgFDIap/rgtM3EwuFDpE9wkfM
+K64UwV1ZSlygBwYFK4EEACKhZANiAARSJmbXE4/ONrLZXFRyxQRcUxMe5bt41vWm
+Qr3dK/X1DSmei20T4epdaCeKMwK58O163kAVHOaDXfRweUTSfI5dZ1l2OXFwQOzH
+Gayma2JpPs8TYR+lC/pDC2iZMp4CR0M=
+-----END EC PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-384-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-384-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-384-KeyPair.pub
new file mode 100644
index 0000000..93ceef0
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-384-KeyPair.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBFImZtcTj842stlcVHLFBFxTEx7lu3jW9aZCvd0r9fUNKZ6LbRPh6l1oJ4ozArnw7XreQBUc5oNd9HB5RNJ8jl1nWXY5cXBA7McZrKZrYmk+zxNhH6UL+kMLaJkyngJHQw== root@osv-linux
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-521-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-521-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-521-KeyPair
new file mode 100644
index 0000000..43f79d7
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-521-KeyPair
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIBUacw+zn8Mw0PYaqqtAlOyaVXARegI6sK5YBhl5E1l9sqTzVN77ce
+1RrqQ8smfvZ6Hiw5gdGcPTszbiorVV5npg6gBwYFK4EEACOhgYkDgYYABACg4siC
+q1iqr4U/spXmw6b2VwBMsof7XLQGoD9wfwUikb8XWthNSmPP1nL6rlzJ5j8Bezn9
+BSSDfVAJfgqxmGIHdgHRVc0mkdq1/Q/DKhBgRyjZc29eo0o2ck3SNGNVaAabRYj6
+ck/iub/U6trKM7bdqy/joYYMwZdxLyYW5YxkPbqEfQ==
+-----END EC PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-521-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-521-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-521-KeyPair.pub
new file mode 100644
index 0000000..520b64e
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-EC-521-KeyPair.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACg4siCq1iqr4U/spXmw6b2VwBMsof7XLQGoD9wfwUikb8XWthNSmPP1nL6rlzJ5j8Bezn9BSSDfVAJfgqxmGIHdgHRVc0mkdq1/Q/DKhBgRyjZc29eo0o2ck3SNGNVaAabRYj6ck/iub/U6trKM7bdqy/joYYMwZdxLyYW5YxkPbqEfQ== root@osv-linux
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-RSA-KeyPair
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-RSA-KeyPair b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-RSA-KeyPair
new file mode 100644
index 0000000..afc6aa8
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-RSA-KeyPair
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEoQIBAAKCAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2c
+tGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZB
+gIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlE
+aaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl
+5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwh
+V+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMwIBIwKCAQALW02YHN4OJz1Siypj
+xoNi87slUaBKBF/NlkWauGUIcpZFMTwnkIn6vCz5MhRbQC4oadRDzFNUrC/g7HdH
+prlqYe2P7uEGIfMb3YNFdk3tgOHmRsHqFgFMpVWsOjlTxNTUsQ74N3Isuxnha4wY
+9f90sBULc+WRdRvO9jbkSDaqoYVKAqCFWtocL+ZWwBXWrIrsQW4PElgZ/duc5DX7
+eeJ5DXCSj9dO+1KxsWEOKaoeABEegrRVys1/shcDNPhf/p0QShKIdPcpnDUc8cny
+1bq8GSt6jEQ+tuRoSnYrY+RD+mlkHrx373Xc1a9woV+QKTThmd9TQ8gzHMHNqq0a
+7kR7AoGBAOuPOTRiKaDtQyMTEX7eeHsPNE24EgvONjNpxyQ6gKGthG5SiB0IO7mP
+r7EggbR2EY8eMCY5HjvxzxgH86n2Pqbzsr6UlQq7YTPupCm/7fPgRknu917GA20f
+1cuY8B04Jp4FIGryBmCcScX6usXXhjfAvYCWWfkSytA8gX9+b1TNAoGBANf8shbp
+wRnQfgAzw2S+xs29pdwa6Jb/xuLvHSyklmgidrK4nsVI8G+zeCqwkqkNM02sM+vR
+c8EX7+myrGf+S2V3JS3AMNXEhavrWVH0CuqFHlBjSwHZ0uKuPpWHlCnud+23AdQz
+Bf1H7tYKt5es3J/B37o4YxhAL6U9qq+ewZH/AoGBAOTURjLjA94oT9jt870SoOyS
+bVLQEYfPol3UeE8UQnEsN4Ec+UDGK2PNaNfzsTL2WjNB5aF5UJIA184znD60seQC
+raMxQFORdF5ViYekgMEFwJ+XrnlSpD4e7PGqgtqOUWZOH33VKsRADSa5DTU3xDYo
+8porp9wDoHKD64MqXYWTAoGADFeVJeF4v6WDivukw+2k9dBSntz3WRXuG5iilNY0
+evqnsnDzITdeMkTFCcDycA9iBHA9ezCKRYxW2id3kOn1rgbO7KvliI7jESNkMJGa
+OUlvck7RFgxyc1pp+ep9fr0rbKtfMLJ1Xu4q56jXSn7oCSEFeFr+WSg9PKRwJ0rm
+fV8CgYAkYOiNY8jH5lwwoPWOIPJg62zdzmRQktrU3z7Nzk5udN6JnG3g57QjkozX
+AgHARKQ2MuXW9OfOnYNhbGeavcBQmg5XUx3eL4PRFw8mFZdjpBD/kM/dfCEwEta3
+FpRlVGn0RNqVV5xxClObD/CikkDqKZG4MSj3CrO3JK33gl1Lgg==
+-----END RSA PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-RSA-KeyPair.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-RSA-KeyPair.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-RSA-KeyPair.pub
new file mode 100644
index 0000000..9fac6f7
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/SecurityUtilsTest-RSA-KeyPair.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxr3N5fkt966xJINl0hH7Q6lLDRR1D0yMjcXCE5roE9VFut2ctGFuo90TCOxkPOMnwzwConeyScVF4ConZeWsxbG9VtRh61IeZ6R5P5ZTvE9xPdZBgIEWvU1bRfrrOfSMihqF98pODspE6NoTtND2eglwSGwxcYFmpdTAmu+8qgxgGxlEaaCjqwdiNPZhygrH81Mv2ruolNeZkn4Bj+wFFmZTD/waN1pQaMf+SO1+kEYIYFNl5+8JRGuUcr8MhHHJB+gwqMTF2BSBVITJzZUiQR0TMtkK6Vbs7yt1F9hhzDzAFDwhV+rsfNQaOHpl3zP07qH+/99A0XG1CVcEdHqVMw== lgoldstein@LGOLDSTEIN-WIN7
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-128-key
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-128-key b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-128-key
new file mode 100644
index 0000000..2b93a42
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-128-key
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,D41AC063160FCC09B1E1931FB43BCEAA
+
+V218dGT2pUpimnwEVn+2ljvK6mvm2aNLlaakMunlHfIswrakJ1WTs8a61pYILOn9
+MGHrCiqe6ZI7FBJ2wXpSxhcuM3fzk6/dW4Ghh4EHG1Y94w97EizxNfyz/iI2XQw0
+i6ttaDLVzP8UcSRElqG+Zpe1A7EE/DkdkXD3f/DaGHtu1zirVeaEIggMLjfTdwnR
+sH9VnUZhe74VdPV0x16h7JjLt5fcbIjqJ6NWW4QvQpPBv3k0oiUy/nP4FXg1b7VW
+7SowuCPi+mF821hj4xSO8ETlAU1eZdtgXqtejtKm0iDtsjnTBZPvDDrq5ephBlxO
+k7JBJG1LFUiDIGnpxos5nCsKEo8UAw9a5/D4xE3C6UTocXon28XGzVCbkZBN6jcd
+UbpjCVwKMJmFL97487u9S57xrGTmJdi1AtF9Rei8juTTQY4+r3l2c7JtdtcbLUhj
+iLvdYnbh6kUEyE19/+omJaWGQlFhYp7ZMRRQSiz6TD8lhSIBPpXzs+uMfhkrifVk
+3WpjRoikmPOOFLtecee5Rp+SpGd700XgLnxwZ47l0FNfrKKqd3+nZX4JILQ2M0JP
+sBx8gcIew8aUqMzWrwZxbrt9Pd1+2kSNVG9hpLoNoA4WpQnYQMo4L0eTCeMNUOap
+f9H0Hh3QnqXTPHbcYZJCGE2RUxLzn/d7rUxUdEzER+pkhJcw9JbV/izTrpDHs9bM
+cfBLggQvs+UIBww2OFz2BztwoQzsSEuNW/SxG/y6SfRUQq5TZw9NxYnrrqfBXKtx
+svB1JVbn2fKq2Lvi8AZ1fF3tyrNot/tptDf0yDHejWDUvVx5cXsKVK2BbVjbZ88k
+mBtUbw7ea9Ev7ZsihNB2EdhPjLhhKlKLIZznPKeXL3GDTXqCgCxTVh4wLvaR8rDU
+C3Isil4WprCeynmZpOe7bxAZDm2QCobnDB8sLQqBI4zgH8X/1iyXJVdSKfK9vxcB
+sJ5pYCcS2q0C+CJkn6HVTlMQ5CyyzvPaDJukJoxwxsZ5hgCsUHFzrvyGnXqGfTBD
+qEW+oA7cj48CfweV5pXHj+mZpCrpn1zRVJRz4h1FZRsttPGtBRAlns5I3kh5BPRs
+4m1BO1jiWyp/7HkUrDRhEf/QeJsP+mTH32pQgnngZ/AGA0PUcKanMUpe1d2ju83V
+EIcTz9ycTHPiOAM6GaVt54fKj9WRBU+7pf14ZdJmfhp6twc0jNtaTh+/I6Pfb0jN
+0d6yKV//pOeJJBNhuOJgm/0vfkOnOojIJchOQCRt5Lg/a4fD/JXtLOed2zOQa+0J
+3d8Y93mQX/iN1wi95/sG79YBYF3FkJYVhjosSKbiIaxIn76zIx4IAlziycDKvgpr
+JgZcVvCDc4flwrf3Cv/uHK7UWOE+16X1CfAy8JzFg5bhiMmhgsJyHmd+zDGrY6NX
+zz+wLmwOenEwC40gpt89OXbgMcwJMtfiSusatRtZ+AAs0jb/8jExVXfcYE3m3r1/
+FqLZ7seTQT2D01YoPlwUtSPxzaZbziAJ/NaGmURnBGVibDCJxwUAiOSIQH4prIfg
+Q2FCJeMTbLV43Lanlby5nrmLkzsw3uo1MO8Of1DbcnVUHNSwrp/nNzrYdxBLIvqS
+-----END RSA PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-128-key.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-128-key.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-128-key.pub
new file mode 100644
index 0000000..b1d66a6
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-128-key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC/oDbs/yYxBdT02ldP6JIrcETJQ+TCml1tHYuo8cIQp0DZCgRZiEZ4foucAT8R/vLK01nnjRzrI42MXiCzyAHb1sPRD0Fsbpa4TFJczPBBRM2mp56airnArQUMmg/ZKlOf82hn+u7Kgn+ljyjYG5FrdoUBju62i0H4+oBfX+pTkd5ruUgqLyPUC3qtNLwjS4PIPAda/pfpsi9UawQ4ommWCCLlwK55NiSrPDBwKNuVWROcQps2NZRxzRLQEiiCEVBEdiUqqUQ+dg2beLV/4cCS860ZZRvCfe+ko8TUBJ7SLtcrvOEYJOKIZDVhcnQKN/wyXCHExSYytUconlFn/9YX root@devenv-aas
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-192-key
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-192-key b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-192-key
new file mode 100644
index 0000000..014410d
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-192-key
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-192-CBC,EDF8E3E634D2106991113815A3C1F11F
+
+PZaaf2TCSCRmPZDnUa9Hq+RVGAXRXl8T8rduiqFLKG2Vk192AEo4WRPWhIcJ4ov+
+o6N69V0Fj/NrRyuwjihQQM27gTK2JK8cvDen+WB3mp5wmfFaazHXO21xNGMAp3bm
+obEeO+NHaG0080yxQ4AU2BzPZ1q/ePjDGBiENUsBAHeZvN2fM5uI4Cs9pqko2OM/
+ZiJQkn2QlnpSPm8B7kspqpEV9bn1peVYAMIkp/yES18Kw50kan3R2CqccutVQfdP
+8ddTPPUvWUrDSrW/Ae2qZvRoCXVLDhy1jBKQDMUCrxqDJCo58GR0FWxK4nhCgfeL
+wmxGaKL/3uKDl5otkj93C1qCbEvQCJm5rcwEjkRVfeSPvNNh+WQrJE7uNcJB4kXX
+tF8c5peZI8ymdlFFXlCqjVpdhJh0YKRhpxhWKLA15I1bbwwY4/mwIIMyXOcAQo58
+G7XOwBPw01/lSJ/YTR8y1ubFbWJ6LgoLgSzsOLnBxJJji148UiNDPPbal0FN8vK2
+VsSRBrjrNHSmh+BSA0AStq4QtQpwNMU0qKFLzx5HvoYiaSG+OKtJLZNqTGzDG5aS
+Xe8rNoTQTaZBy5yTY1zTn2lMFngWSt/aYyXfc1BXdYyTOk/eoGHtXxp91nQtCdkG
+4VMMfPlnbW1Q2Cq4ATdcIUPPKLaNVlP2TbloBb1wYjXy8RfFFqZVNOLnxXnXOXUG
+U5SlG44XV5AouwhrlUPrm+0+9jIh4pO4sfG0ldGwabGbUMZ6vvgcU9w1yRcsjycG
+v9yH7CfFKKXjUL5RR2weZVD8vRjQMbfVHRt3P4NFClI6e1gXx92FmT7x97866e+T
+TohyC7uZQq2prBzt8BwxFP0h19+CiTVzofe2I9x+Hce67F+ATnLq9fxOnjZlwK1i
+K95eK+Itc9zSu9tIZGaLvghOExZ24893Ncz5NJ2nn0YptxSi24e8PcECHhSUrjv9
+1VOi8JUmv58bQ5ocuUlHmB2FkpnsYUNjtQx1OT2IRovQJIDCttHhT3QIGoiqoeTw
+sSwyz0gDYzAJtuVkSZxqXJ4LEMozaA3uwxv0TfaQordm+GuGKuV5MsKyEQtITUk5
+5bgQfS0VQqkbq2fRjtd3nIJWCF5otrVulY0tzoTBjGZ2trNj5nz5ZEUTFdRSAaxb
+F+i3Mv8gGPC+2YIo8feDCHnRQ1AALjLon8nKefRsWAOYUozNCtHvu4g7WK6+GcPd
+eiYM7tbSrNCHg+KdB+eWrJCfVCgVHTj8mGb3EiH6oxgx51TmlyuQYJAVseWjQOdm
+IDKiAsNNZijmgMWLsFZG1VvF/446VWIGkSW9eB6bB7aR1Kg4F18VkXdn+SPmaHrC
+NHhNa9dOmcOvlNXIs7jlTjRVEC0pp8DH0AUdPjqlR/F1IHb7uFyqlza8Cqb7PZJs
+x+9GbSwuyKLKLvMzmsnBtpRT32Srsbx6pWst40gaoOMFxto22vmGxz4IABGi96V/
+NrrDHtuZ5OaePl7GihKcUCSSVcoFO2jKKNMMHSSVGhxkfir7qGzMe5LZ9/zl+bvW
+vBS0wSPUd+P3mLc0m53mH+g26I479ryX6/2W+GecB1h5p7S7vJMPQjqZAJskogf5
+-----END RSA PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-192-key.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-192-key.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-192-key.pub
new file mode 100644
index 0000000..3fda036
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-192-key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCutvykV6G6t4JlLhlJOQKknSB++PXLTtuSjXaH77hUUEl/yc9VbEsfOz1NySBRQDWT0sgRZ1D2YAxaIvK9+0wZPnRNO1ptDrwj650Gflh8vKkEQOlHH7ISTxQTHtyHGeXfm3Bl+qqxTMPG5gcxxkKINMvKAkNYBnOyVX1OXUvqtM1jx6THlfWPQ9yFuVmZDVj1W5VItxjG293RejbjN1EjhhGRBiqwczcIPgPiMDfR0+xFNEQt6YD4k9TFEkLmIfCn+fcqKJhP/z99bRosMHtkRwfX/Rco+G9dRwe1sHvVH7ETeS7v3vl3WoM58AlpYYGUIXsxozRdGE93AmTkaSIj SecurityUtilsTest
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-256-key
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-256-key b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-256-key
new file mode 100644
index 0000000..26b699e
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-256-key
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,4FF564F3E05187BEDEDA0529D44EE13E
+
+z7KGBVkxJzNS1Z8vMurcYmHhlbxnUYHry87VgzV38UkwsLSo5FIPNUaxvLPeD3Im
+YW4hQ/1Z+3onSwTgaUqxih6YK+fHS0RZD5R4HjOWaERCPZa5/oDciLrjOU3EKkSV
+n84/ezqUn8GlMJYcpB2sURCmnJZ6erHCVSPAhVlsrWPO9uZHx8BpaclqsvC4Gq7a
+C4liGjDNsFvVO998eDIDLmHeO+qGnyeQK+pu3/+Ausvd9QjUf4L3T+Lu+Cgr0w59
+tB3TxeGxPel9Av88VYIhCddIEvRyMcN8lzLrrYrsLNPMmaF1x2uNDORJCTlEjvLw
+QG6zugofiUVdZdsWRPFmM8DbujeoUHkPkwImXedhiKz1Tp0Rz6gqGx6tzsqEe0kD
+eS2e0Ibj41fFxpkQlaG/xDliUvntkoQ6v7ZKbXQrsBP4hyoMcjeUMhWL+5shkJEV
+RzvZOl7WKoovFnf3glLdiDFqlfLK5BhBJckLmdmnVvsD1HYeKqSDrx+r3hUF3yZ5
+Obxxk++QTC1qHhnyAwqW7Y2owq81pmOhgxKqh/4OoxYV0trIgzI47+5hKsp12Van
+7qBCfTtXv4IlBKaKARTroQ2sDPAi1B2j8mU5lrGm81BzsLSqh1IJjOMsccWNfjMS
+jQMKvcQN94y7AvxcBDGVn9FZftNWs9/ZwitRCFfrz/n0u6hLDQBbH6muDffo4ZhF
+ZiH23W743N/ZASC/kA7LZ3pmH6TNl2Zf1gP+tTrfXu17BRDJYi8VqHBcuck/M4Ox
+qOpP4mK2B/aAVmTkty67+QJerIwsW+h8LsjkQTnXGoneobsx2nL8OvJtPHkOUo4b
+1zFyFvQK+V8DWWAml4Y8V+4NCgrybqqK11ApRl7FfaOyCMgZ+AvqzjK3E5s1gsaf
+AaxORV0iDE0Y8nhq9EEhDNTr5Yi6FeHuRmVsack1AQqYn1U64k21x4ZEmDjslq9t
+WKizqG6jz6xtRtsypr0oCBYirt8p5qDmeO58MdES6A+nkOZC0ysyhizpDWdj01rQ
+6w3el0t/wUh1lBC3UCTeD7Vz20B1rAs5AlbdFmOUGHoH2Hjty1DQVZ+Fl5/K9AQi
+yaVR07IDE45i9rvl7DThRHT6Qd0KAb7bIo+Xlz/xKUgA0UVLkpfGtgDNi1DSTsBQ
+m6xlvR9M3tmMwnHlOEfX8YAGIQR9JLiImUtypjXnp6IdxCn682UIWC9XakqMFbzS
+Hm4Y0VBHYPisByl7PuavEP69pgYVdt1PqFFOc3xL42QVihYsM9huw1j4rlP7/jXB
+zJyH6yuqZHV7BYmurBByxRCe4joN24D6HYWTLKhPjgIG++n/IZ+PO7s0khrTh3Pl
+L3bOujyLtr88w6PPhBOCVDPgmJEC6lIGC8zhh+S9Knewr50PsvOoQVKGDTSPO+G7
+NNa/ugN9/eTeO2ru2y6HuahWrf2seKrTQB8JVYP0SZkOe4zVdMRR5M559iLIdh0e
+U/hrj6v/dYVbIkmSWdhtxx0jXbM2VlWTeQQjUYzeVumVhZ1pk2P0LeHrSWhHa5h2
+hadw69Ht0hRTbljZ1MHjK9BbQI1Ph+YBuDr0aPVBf2ePYFvPObw2Mik7ctiDlmQT
+-----END RSA PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-256-key.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-256-key.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-256-key.pub
new file mode 100644
index 0000000..6a3a0ac
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-AES-256-key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNUSajd7RzgqvQ2Q2em3CFA4Fh2f8YJkzJTPrcn9L9SFZnent2rABlVVr216jh3hAjhRFEkRhsoWUxUB7UYN6Ox/hGXrfCtD9wB0QbpOjNb0tnSovl3e+LfnWO5mpaRAb9croiuKVwQXuiwLNOQqs5C2wvLJhc77U4myuvFycYQ9Gh9g12g05Q8Qs+Du6hFSV1n1ZDMTtiVYknhe2gCLmBh1ghpaGeG3eWpd8EXumTrpwrylb1hJtj4yo748dXFa/YUJpqzCNyoA+0aY+v0MwgxZcS6zWt5VQ3yqRi4YrXX7vvhz80rKmJQlEhvM9rVKAwKc/qkJdTWlU+v0+/zY7/ hello@world
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-DES-EDE3-key
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-DES-EDE3-key b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-DES-EDE3-key
new file mode 100644
index 0000000..e6f6156
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-DES-EDE3-key
@@ -0,0 +1,30 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,7DD956370CB0B83E
+
+oTBNWVUGIKQ3LX/bIE+upuTtiVSWQ9ooTxdNJRIpGsq6oTevtxgkuIF9oO+ShZJq
+zf/mTQPtAyRHSX8JsTKw9GZhd5w7c07V3wfzx689RHFCZDW1DIl67KvwyhRwfRtY
+HXZGYPW/XmOgh3Gw31csmLlJd42r8d+7MRIfmo25SO5Sy67mNccRJikdbYa/HH50
+yMVaf7o73tLLIS3WNnhaKjf9hRsSW370aICk3hfS/NMriae0xAMpqpYQmGkn9Zhh
+zTg+Kjo0FiEZEnMNKZQRRWx7VsoQRfrebOZq3LF7tfEnJcKneJ+grvQp0FpkEMzN
+JeiaEwtoIrXRU5KENQbEnXqF2Mpai6ejh5lVflcHqI01lwWGWkwxnNpxgMbWCKuB
+Unad3fQQLFQU67B8jQpES2AQVXO7UaaDHNtATftC2gFpBktlkSurT/KZBT4bVfS7
+z3K9gFB/lYrji2ef+xuzbLmq+eja9OlZIVxH7RTxODkyrxeNhtEUXJgezOB64Cny
+YFrsDPcR8rSRc5SjMMuAy2dSkyndSp3m2YmtVFHibPrX18K2lblRkiuVDci0EX4p
+1XzaCJ9mhdtMedpIzFdwgCOt/M8OBw9g02qgOK1uBu37tXdACNxhX/wrFPCtbJjD
+yBwnBbXoeca8ZF45uw1egKlHaY2d7kTsH+bdx04bTo4Xq0+lXMALxxkfwTOt27xL
+JQp15AFm6ZAshVt7hxCtm512HoSkkFYMPUPawH9HVZE6f4sbNBlbqgCmYhD5HPMN
+Kd3ssQWTXwiOSd+k8HuBUBhtdZsgdpSdR5xH9wEymS9fcGmrf2yLuRlxhvoqo44X
++go5CqLoAKoUtVJdtZrkmMdF1OxufDBZ16+fKN8NNE8vQv8Kt221yg/mmDy3t6mh
+axynBSxk1Qtjz2H9iZJ5nglF2Hceb1FQXHGlLCNv+cwA9vRC0pQJ25fPHVhvxhmW
+gnLJ5OY+zXENUWvMIeAGCSfjSVtnSVWAfdJ7kgHZc/wCWO6rYAwlfsPDq8ZdQ8Za
+4Hm6sUnkHHtagvJZahjdSPoziE68FiHqFU3PdmuQsZpCFOzloxahWImgx0UM9n8k
+2DlRPK6bMBCjpUrb5D0jp8YU2KDepw2D4O7RaFLh47w78oxrO1gABY23M4JQfUiP
+lzsfsGy9OI1twCcsJhFOX95nL9w+hlvFALTkY5UT3dB3eWP3k2nE12UxQhE3hVZ9
+vI100y2m/FUnKkeGYkvhRw9yD611jjYuRYGL3LsKNaOK97Ses+vAB0LNOss+12vz
+7bYvDUVJ5csfpPGqxyzYwIbytDwUcvxTLxRlgQNb2YMqzSmVodO0lyXdNF1/KUFF
+nIdlh0KSe4BALzYhya9V71MngLHFifp/erS4gdnFbbBCmZTEZhziBnqimRPoN5Co
+K+22blcRkDytiL+NTRybvfOYvy6Ulb4OK/So+Y5r1B5KGhJqFWSeo0g2ftAB6r1h
+HSbfoxYO/xCMQy7aNXuJI0giVRThcQsQapTO2DMeymt/0nj8qEPF0dubtbmgJ4dd
+aZuYaY1Qxk0umVfq9zHsRTbRb+16TuZtp+7brDDRpRe6lSzdbTNShg==
+-----END RSA PRIVATE KEY-----
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-DES-EDE3-key.pub
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-DES-EDE3-key.pub b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-DES-EDE3-key.pub
new file mode 100644
index 0000000..f5c8c85
--- /dev/null
+++ b/sshd-common/src/test/resources/org/apache/sshd/common/util/security/super-secret-passphrase-RSA-DES-EDE3-key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxvz5QWkCxznYbBOUbMFsCf+PJLauyZTcPsHLPZ/lHYH7EIa9s8NWbfFyTJXVwcuJiDCoRZ8d7KBahAuNjGeXsuJQuCqlLLl6GKoFnI0hnbKbFHDDSub+s3WvLdaTeF22qlTmlPb3RFYUO0cWk9MVpVNM6ev33CUGv4Dmbr0dZNkgUOkcPHiQpxKizsED+EWSVh1Ptx8AEDObNyFHJljbHUeJrHTIlcaekJheRXQWLvsJqKD0TN+Dkvi044MDWG9VjVyNsyXBCz1Vk9VaK1dNkkH+RDGTsFvFj7IPwBS/FliEVRrKOUgjMrmKUPbAm8IblIsno+HLZ60OB6X6hu2iHw== lgoldstein@LGOLDSTEIN-WIN7
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-contrib/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-contrib/pom.xml b/sshd-contrib/pom.xml
index 672069b..ac00587 100644
--- a/sshd-contrib/pom.xml
+++ b/sshd-contrib/pom.xml
@@ -67,7 +67,7 @@
<artifactId>eddsa</artifactId>
<optional>true</optional>
</dependency>
-
+ <!-- Test dependencies -->
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
@@ -76,6 +76,13 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>test</scope>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-contrib/src/test/java/org/apache/sshd/server/scp/SimpleAccessControlScpEventListenerTest.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/java/org/apache/sshd/server/scp/SimpleAccessControlScpEventListenerTest.java b/sshd-contrib/src/test/java/org/apache/sshd/server/scp/SimpleAccessControlScpEventListenerTest.java
index 4312dda..ecc9678 100644
--- a/sshd-contrib/src/test/java/org/apache/sshd/server/scp/SimpleAccessControlScpEventListenerTest.java
+++ b/sshd-contrib/src/test/java/org/apache/sshd/server/scp/SimpleAccessControlScpEventListenerTest.java
@@ -36,7 +36,7 @@ import org.apache.sshd.common.scp.ScpException;
import org.apache.sshd.common.scp.ScpHelper;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.FixMethodOrder;
@@ -94,12 +94,12 @@ public class SimpleAccessControlScpEventListenerTest extends BaseTestSupport {
ScpClient scp = creator.createScpClient(session);
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path scpRoot = Utils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
- Utils.deleteRecursive(scpRoot);
+ Path scpRoot = CommonTestSupportUtils.resolve(targetPath, ScpHelper.SCP_COMMAND_PREFIX, getClass().getSimpleName(), getCurrentTestName());
+ CommonTestSupportUtils.deleteRecursive(scpRoot);
Path remoteDir = assertHierarchyTargetFolderExists(scpRoot.resolve("remote"));
Path remoteFile = remoteDir.resolve("file.txt");
- String remotePath = Utils.resolveRelativeRemotePath(parentPath, remoteFile);
+ String remotePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, remoteFile);
byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
Files.write(remoteFile, data);
byte[] downloaded = scp.downloadBytes(remotePath);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-contrib/src/test/java/org/apache/sshd/server/subsystem/sftp/SimpleAccessControlSftpEventListenerTest.java
----------------------------------------------------------------------
diff --git a/sshd-contrib/src/test/java/org/apache/sshd/server/subsystem/sftp/SimpleAccessControlSftpEventListenerTest.java b/sshd-contrib/src/test/java/org/apache/sshd/server/subsystem/sftp/SimpleAccessControlSftpEventListenerTest.java
index ddbe162..f824811 100644
--- a/sshd-contrib/src/test/java/org/apache/sshd/server/subsystem/sftp/SimpleAccessControlSftpEventListenerTest.java
+++ b/sshd-contrib/src/test/java/org/apache/sshd/server/subsystem/sftp/SimpleAccessControlSftpEventListenerTest.java
@@ -38,7 +38,7 @@ import org.apache.sshd.common.subsystem.sftp.SftpException;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.scp.ScpCommandFactory;
import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.Utils;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.FixMethodOrder;
@@ -89,7 +89,7 @@ public class SimpleAccessControlSftpEventListenerTest extends BaseTestSupport {
public void testReadOnlyFileAccess() throws Exception {
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
Path testFile = assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
Files.deleteIfExists(testFile);
@@ -103,7 +103,7 @@ public class SimpleAccessControlSftpEventListenerTest extends BaseTestSupport {
session.auth().verify(5L, TimeUnit.SECONDS);
try (SftpClient sftp = SftpClientFactory.instance().createSftpClient(session)) {
- String file = Utils.resolveRelativeRemotePath(parentPath, testFile);
+ String file = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, testFile);
try (CloseableHandle handle = sftp.open(file, OpenMode.Read)) {
byte[] actual = new byte[data.length];
int readLen = sftp.read(handle, 0L, actual);
@@ -139,7 +139,7 @@ public class SimpleAccessControlSftpEventListenerTest extends BaseTestSupport {
public void testReadOnlyDirectoryAccess() throws Exception {
Path targetPath = detectTargetFolder();
Path parentPath = targetPath.getParent();
- Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+ Path lclSftp = CommonTestSupportUtils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
Path testFile = assertHierarchyTargetFolderExists(lclSftp).resolve("file.txt");
byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8);
Files.deleteIfExists(testFile);
@@ -153,12 +153,12 @@ public class SimpleAccessControlSftpEventListenerTest extends BaseTestSupport {
session.auth().verify(5L, TimeUnit.SECONDS);
try (SftpClient sftp = SftpClientFactory.instance().createSftpClient(session)) {
- String folder = Utils.resolveRelativeRemotePath(parentPath, targetPath);
+ String folder = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, targetPath);
for (SftpClient.DirEntry entry : sftp.readDir(folder)) {
assertNotNull("No entry", entry);
}
- String file = Utils.resolveRelativeRemotePath(parentPath, testFile);
+ String file = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, testFile);
try {
sftp.remove(file);
fail("Unexpected file remove success");
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-core/pom.xml b/sshd-core/pom.xml
index 0bcab83..c81d75e 100644
--- a/sshd-core/pom.xml
+++ b/sshd-core/pom.xml
@@ -39,9 +39,11 @@
<dependencies>
<dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${project.version}</version>
</dependency>
+
<dependency>
<groupId>tomcat</groupId>
<artifactId>tomcat-apr</artifactId>
@@ -68,6 +70,14 @@
<!-- test dependencies -->
<dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-core</artifactId>
<scope>test</scope>
@@ -117,11 +127,6 @@
<artifactId>ganymed-ssh2</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.apache.servicemix.bundles</groupId>
- <artifactId>org.apache.servicemix.bundles.not-yet-commons-ssl</artifactId>
- <scope>test</scope>
- </dependency>
</dependencies>
<build>
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/filtered-resources/org/apache/sshd/sshd-version.properties
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/filtered-resources/org/apache/sshd/sshd-version.properties b/sshd-core/src/main/filtered-resources/org/apache/sshd/sshd-version.properties
deleted file mode 100644
index 2c32c28..0000000
--- a/sshd-core/src/main/filtered-resources/org/apache/sshd/sshd-version.properties
+++ /dev/null
@@ -1,23 +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.
-##
-
-groupId=${pom.groupId}
-artifactId=${pom.artifactId}
-version=${pom.version}
-sshd-version=${pom.artifactId}-${pom.version}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
index 533cae5..76f2350 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
@@ -25,6 +25,7 @@ import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.nio.file.LinkOption;
+import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.util.ArrayList;
@@ -47,6 +48,7 @@ import org.apache.sshd.client.auth.password.UserAuthPasswordFactory;
import org.apache.sshd.client.auth.pubkey.UserAuthPublicKeyFactory;
import org.apache.sshd.client.config.hosts.HostConfigEntry;
import org.apache.sshd.client.config.hosts.HostConfigEntryResolver;
+import org.apache.sshd.client.config.keys.ClientIdentity;
import org.apache.sshd.client.config.keys.ClientIdentityLoader;
import org.apache.sshd.client.config.keys.DefaultClientIdentitiesWatcher;
import org.apache.sshd.client.future.ConnectFuture;
@@ -68,6 +70,7 @@ import org.apache.sshd.common.ServiceFactory;
import org.apache.sshd.common.channel.Channel;
import org.apache.sshd.common.config.keys.FilePasswordProvider;
import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
import org.apache.sshd.common.future.SshFutureListener;
import org.apache.sshd.common.helpers.AbstractFactoryManager;
import org.apache.sshd.common.io.IoConnectFuture;
@@ -769,4 +772,61 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
public static SshClient setUpDefaultClient() {
return ClientBuilder.builder().build();
}
+
+ /**
+ * @param <C> The generic client class
+ * @param client The {@link SshClient} to updated
+ * @param strict If {@code true} then files that do not have the required
+ * access rights are excluded from consideration
+ * @param supportedOnly If {@code true} then ignore identities that are not
+ * supported internally
+ * @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 LinkOption}s to apply when checking
+ * for existence
+ * @return The updated <tt>client</tt> instance - provided a non-{@code null}
+ * {@link KeyPairProvider} was generated
+ * @throws IOException If failed to access the file system
+ * @throws GeneralSecurityException If failed to load the keys
+ * @see #setKeyPairProvider(SshClient, Path, boolean, boolean, FilePasswordProvider, LinkOption...)
+ */
+ public static <C extends SshClient> C setKeyPairProvider(
+ C client, boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
+ throws IOException, GeneralSecurityException {
+ return setKeyPairProvider(client, PublicKeyEntry.getDefaultKeysFolderPath(), strict, supportedOnly, provider, options);
+ }
+
+ /**
+ * @param <C> The generic client class
+ * @param client The {@link SshClient} to updated
+ * @param dir The folder to scan for the built-in identities
+ * @param strict If {@code true} then files that do not have the required
+ * access rights are excluded from consideration
+ * @param supportedOnly If {@code true} then ignore identities that are not
+ * supported internally
+ * @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 LinkOption}s to apply when checking
+ * for existence
+ * @return The updated <tt>client</tt> instance - provided a non-{@code null}
+ * {@link KeyPairProvider} was generated
+ * @throws IOException If failed to access the file system
+ * @throws GeneralSecurityException If failed to load the keys
+ * @see #loadDefaultKeyPairProvider(Path, boolean, boolean, FilePasswordProvider, LinkOption...)
+ */
+ public static <C extends SshClient> C setKeyPairProvider(
+ C client, Path dir, boolean strict, boolean supportedOnly, FilePasswordProvider provider, LinkOption... options)
+ throws IOException, GeneralSecurityException {
+ KeyPairProvider kpp =
+ ClientIdentity.loadDefaultKeyPairProvider(dir, strict, supportedOnly, provider, options);
+ if (kpp != null) {
+ client.setKeyPairProvider(kpp);
+ }
+
+ return client;
+ }
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
deleted file mode 100644
index cfd1f85..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/AuthenticationIdentitiesProvider.java
+++ /dev/null
@@ -1,125 +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.client.auth;
-
-import java.security.KeyPair;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
-import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface AuthenticationIdentitiesProvider extends KeyIdentityProvider, PasswordIdentityProvider {
-
- /**
- * Compares 2 password identities - returns zero ONLY if <U>both</U> compared
- * objects are {@link String}s and equal to each other
- */
- Comparator<Object> PASSWORD_IDENTITY_COMPARATOR = (o1, o2) -> {
- if (!(o1 instanceof String) || !(o2 instanceof String)) {
- return -1;
- } else {
- return ((String) o1).compareTo((String) o2);
- }
- };
-
- /**
- * Compares 2 {@link KeyPair} identities - returns zero ONLY if <U>both</U> compared
- * objects are {@link KeyPair}s and equal to each other
- */
- Comparator<Object> KEYPAIR_IDENTITY_COMPARATOR = (o1, o2) -> {
- if ((!(o1 instanceof KeyPair)) || (!(o2 instanceof KeyPair))) {
- return -1;
- } else if (KeyUtils.compareKeyPairs((KeyPair) o1, (KeyPair) o2)) {
- return 0;
- } else {
- return 1;
- }
- };
-
- /**
- * @return All the currently available identities - passwords, keys, etc...
- */
- Iterable<?> loadIdentities();
-
- static int findIdentityIndex(List<?> identities, Comparator<? super Object> comp, Object target) {
- for (int index = 0; index < identities.size(); index++) {
- Object value = identities.get(index);
- if (comp.compare(value, target) == 0) {
- return index;
- }
- }
-
- return -1;
- }
-
- /**
- * @param identities The {@link Iterable} identities - OK if {@code null}/empty
- * @return An {@link AuthenticationIdentitiesProvider} wrapping the identities
- */
- static AuthenticationIdentitiesProvider wrapIdentities(Iterable<?> identities) {
- return new AuthenticationIdentitiesProvider() {
- @Override
- public Iterable<KeyPair> loadKeys() {
- return selectIdentities(KeyPair.class);
- }
-
- @Override
- public Iterable<String> loadPasswords() {
- return selectIdentities(String.class);
- }
-
- @Override
- public Iterable<?> loadIdentities() {
- return selectIdentities(Object.class);
- }
-
- // NOTE: returns a NEW Collection on every call so that the original
- // identities remain unchanged
- private <T> Collection<T> selectIdentities(Class<T> type) {
- Collection<T> matches = null;
- for (Iterator<?> iter = GenericUtils.iteratorOf(identities); iter.hasNext();) {
- Object o = iter.next();
- Class<?> t = o.getClass();
- if (!type.isAssignableFrom(t)) {
- continue;
- }
-
- if (matches == null) {
- matches = new LinkedList<>();
- }
-
- matches.add(type.cast(o));
- }
-
- return (matches == null) ? Collections.<T>emptyList() : matches;
- }
- };
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
deleted file mode 100644
index 81c26ca..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/hostbased/HostKeyIdentityProvider.java
+++ /dev/null
@@ -1,53 +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.client.auth.hostbased;
-
-import java.security.KeyPair;
-import java.security.cert.X509Certificate;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface HostKeyIdentityProvider {
- /**
- * @return The host keys as a {@link java.util.Map.Entry} of key + certificates (which can be {@code null}/empty)
- */
- Iterable<? extends Map.Entry<KeyPair, List<X509Certificate>>> loadHostKeys();
-
- static Iterator<? extends Map.Entry<KeyPair, List<X509Certificate>>> iteratorOf(HostKeyIdentityProvider provider) {
- return GenericUtils.iteratorOf((provider == null) ? null : provider.loadHostKeys());
- }
-
- static HostKeyIdentityProvider wrap(KeyPair... pairs) {
- return wrap(GenericUtils.asList(pairs));
- }
-
- static HostKeyIdentityProvider wrap(Iterable<? extends KeyPair> pairs) {
- return () -> GenericUtils.wrapIterable(pairs, kp -> new SimpleImmutableEntry<>(kp, Collections.<X509Certificate>emptyList()));
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
index d765b11..479e5bc 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
@@ -25,7 +25,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.apache.sshd.client.ClientAuthenticationManager;
import org.apache.sshd.client.auth.AbstractUserAuth;
-import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.RuntimeSshException;
import org.apache.sshd.common.SshConstants;
@@ -87,7 +86,7 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
@Override
public void init(ClientSession session, String service) throws Exception {
super.init(session, service);
- passwords = PasswordIdentityProvider.iteratorOf(session);
+ passwords = ClientSession.passwordIteratorOf(session);
maxTrials = session.getIntProperty(ClientAuthenticationManager.PASSWORD_PROMPTS, ClientAuthenticationManager.DEFAULT_PASSWORD_PROMPTS);
ValidateUtils.checkTrue(maxTrials > 0, "Non-positive max. trials: %d", maxTrials);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java
deleted file mode 100644
index 6af6696..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/password/PasswordIdentityProvider.java
+++ /dev/null
@@ -1,183 +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.client.auth.password;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.function.Function;
-import java.util.function.Supplier;
-
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface PasswordIdentityProvider {
-
- /**
- * An "empty" implementation of {@link PasswordIdentityProvider} that returns
- * and empty group of passwords
- */
- PasswordIdentityProvider EMPTY_PASSWORDS_PROVIDER = new PasswordIdentityProvider() {
- @Override
- public Iterable<String> loadPasswords() {
- return Collections.emptyList();
- }
-
- @Override
- public String toString() {
- return "EMPTY";
- }
- };
-
- /**
- * Invokes {@link PasswordIdentityProvider#loadPasswords()} and returns the result.
- * Ignores {@code null} providers (i.e., returns an empty iterable instance)
- */
- Function<PasswordIdentityProvider, Iterable<String>> LOADER = p ->
- (p == null) ? Collections.emptyList() : p.loadPasswords();
-
- /**
- * @return The currently available passwords - ignored if {@code null}
- */
- Iterable<String> loadPasswords();
-
- /**
- * Creates a "unified" {@link Iterator} of passwords out of the registered
- * passwords and the extra available ones as a single iterator of passwords
- *
- * @param session The {@link ClientSession} - ignored if {@code null} (i.e., empty
- * iterator returned)
- * @return The wrapping iterator
- * @see ClientSession#getRegisteredIdentities()
- * @see ClientSession#getPasswordIdentityProvider()
- */
- static Iterator<String> iteratorOf(ClientSession session) {
- return (session == null) ? Collections.<String>emptyIterator() : iteratorOf(session.getRegisteredIdentities(), session.getPasswordIdentityProvider());
- }
-
- /**
- * Creates a "unified" {@link Iterator} of passwords out of 2 possible
- * {@link PasswordIdentityProvider}
- *
- * @param identities The registered passwords
- * @param passwords Extra available passwords
- * @return The wrapping iterator
- * @see #resolvePasswordIdentityProvider(PasswordIdentityProvider, PasswordIdentityProvider)
- */
- static Iterator<String> iteratorOf(PasswordIdentityProvider identities, PasswordIdentityProvider passwords) {
- return iteratorOf(resolvePasswordIdentityProvider(identities, passwords));
- }
-
- /**
- * Resolves a non-{@code null} iterator of the available passwords
- *
- * @param provider The {@link PasswordIdentityProvider} - ignored if {@code null} (i.e.,
- * return an empty iterator)
- * @return A non-{@code null} iterator - which may be empty if no provider or no passwords
- */
- static Iterator<String> iteratorOf(PasswordIdentityProvider provider) {
- return GenericUtils.iteratorOf((provider == null) ? null : provider.loadPasswords());
- }
-
- /**
- * <P>Creates a "unified" {@link PasswordIdentityProvider} out of 2 possible ones
- * as follows:</P></BR>
- * <UL>
- * <LI>If both are {@code null} then return {@code null}.</LI>
- * <LI>If either one is {@code null} then use the non-{@code null} one.</LI>
- * <LI>If both are the same instance then use it.</U>
- * <LI>Otherwise, returns a wrapper that groups both providers.</LI>
- * </UL>
- * @param identities The registered passwords
- * @param passwords The extra available passwords
- * @return The resolved provider
- * @see #multiProvider(PasswordIdentityProvider...)
- */
- static PasswordIdentityProvider resolvePasswordIdentityProvider(PasswordIdentityProvider identities, PasswordIdentityProvider passwords) {
- if ((passwords == null) || (identities == passwords)) {
- return identities;
- } else if (identities == null) {
- return passwords;
- } else {
- return multiProvider(identities, passwords);
- }
- }
-
- /**
- * Wraps a group of {@link PasswordIdentityProvider} into a single one
- *
- * @param providers The providers - ignored if {@code null}/empty (i.e., returns
- * {@link #EMPTY_PASSWORDS_PROVIDER}
- * @return The wrapping provider
- * @see #multiProvider(Collection)
- */
- static PasswordIdentityProvider multiProvider(PasswordIdentityProvider... providers) {
- return multiProvider(GenericUtils.asList(providers));
- }
-
- /**
- * Wraps a group of {@link PasswordIdentityProvider} into a single one
- *
- * @param providers The providers - ignored if {@code null}/empty (i.e., returns
- * {@link #EMPTY_PASSWORDS_PROVIDER}
- * @return The wrapping provider
- */
- static PasswordIdentityProvider multiProvider(Collection<? extends PasswordIdentityProvider> providers) {
- return GenericUtils.isEmpty(providers) ? EMPTY_PASSWORDS_PROVIDER : wrapPasswords(iterableOf(providers));
- }
-
- /**
- * Wraps a group of {@link PasswordIdentityProvider} into an {@link Iterable} of their combined passwords
- *
- * @param providers The providers - ignored if {@code null}/empty (i.e., returns an empty iterable instance)
- * @return The wrapping iterable
- */
- static Iterable<String> iterableOf(Collection<? extends PasswordIdentityProvider> providers) {
- Iterable<Supplier<Iterable<String>>> passwordSuppliers =
- GenericUtils.<PasswordIdentityProvider, Supplier<Iterable<String>>>wrapIterable(providers, p -> p::loadPasswords);
- return GenericUtils.multiIterableSuppliers(passwordSuppliers);
- }
-
- /**
- * Wraps a group of passwords into a {@link PasswordIdentityProvider}
- *
- * @param passwords The passwords - ignored if {@code null}/empty
- * (i.e., returns {@link #EMPTY_PASSWORDS_PROVIDER})
- * @return The provider wrapper
- */
- static PasswordIdentityProvider wrapPasswords(String... passwords) {
- return wrapPasswords(GenericUtils.asList(passwords));
- }
-
- /**
- * Wraps a group of passwords into a {@link PasswordIdentityProvider}
- *
- * @param passwords The passwords {@link Iterable} - ignored if {@code null}
- * (i.e., returns {@link #EMPTY_PASSWORDS_PROVIDER})
- * @return The provider wrapper
- */
- static PasswordIdentityProvider wrapPasswords(Iterable<String> passwords) {
- return (passwords == null) ? EMPTY_PASSWORDS_PROVIDER : () -> passwords;
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/auth/password/UserAuthPassword.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/password/UserAuthPassword.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/password/UserAuthPassword.java
index 53ec529..6785305 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/password/UserAuthPassword.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/password/UserAuthPassword.java
@@ -48,7 +48,7 @@ public class UserAuthPassword extends AbstractUserAuth {
@Override
public void init(ClientSession session, String service) throws Exception {
super.init(session, service);
- passwords = PasswordIdentityProvider.iteratorOf(session);
+ passwords = ClientSession.passwordIteratorOf(session);
}
@Override
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.java
deleted file mode 100644
index 51444ae..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/PublicKeyIdentity.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.client.auth.pubkey;
-
-import java.security.PublicKey;
-
-/**
- * Represents a public key identity
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public interface PublicKeyIdentity {
- /**
- * @return The {@link PublicKey} identity value
- */
- PublicKey getPublicKey();
-
- /**
- * Proves the public key identity by signing the given data
- *
- * @param data Data to sign
- * @return Signed data - using the identity
- * @throws Exception If failed to sign the data
- */
- byte[] sign(byte[] data) throws Exception;
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
index 202b0a8..02ce1dc 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKeyIterator.java
@@ -70,7 +70,7 @@ public class UserAuthPublicKeyIterator extends AbstractKeyPairIterator<PublicKey
}
}
- identities.add(Stream.of(KeyIdentityProvider.providerOf(session))
+ identities.add(Stream.of(ClientSession.providerOf(session))
.map(KeyIdentityProvider::loadKeys)
.flatMap(GenericUtils::stream)
.map(kp -> new KeyPairIdentity(signatureFactories, session, kp)));
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/SshClientConfigFileReader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/SshClientConfigFileReader.java b/sshd-core/src/main/java/org/apache/sshd/client/config/SshClientConfigFileReader.java
index ccd8f26..c205377 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/SshClientConfigFileReader.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/config/SshClientConfigFileReader.java
@@ -32,7 +32,8 @@ public final class SshClientConfigFileReader {
throw new UnsupportedOperationException("No instance allowed");
}
- public static <C extends SshClient> C configure(C client, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
+ public static <C extends SshClient> C configure(
+ C client, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) {
SshConfigFileReader.configure((AbstractFactoryManager) client, props, lenient, ignoreUnsupported);
SshConfigFileReader.configureKeyExchanges(client, props, lenient, ClientBuilder.DH2KEX, ignoreUnsupported);
return client;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolver.java b/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolver.java
deleted file mode 100644
index f1c9ea8..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolver.java
+++ /dev/null
@@ -1,107 +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.client.config.hosts;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.io.ModifiableFileWatcher;
-
-/**
- * Watches for changes in a configuration file and automatically reloads any changes
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ConfigFileHostEntryResolver extends ModifiableFileWatcher implements HostConfigEntryResolver {
- private final AtomicReference<HostConfigEntryResolver> delegateHolder = // assumes initially empty
- new AtomicReference<>(HostConfigEntryResolver.EMPTY);
-
- public ConfigFileHostEntryResolver(File file) {
- this(Objects.requireNonNull(file, "No file to watch").toPath());
- }
-
- public ConfigFileHostEntryResolver(Path file) {
- this(file, IoUtils.EMPTY_LINK_OPTIONS);
- }
-
- public ConfigFileHostEntryResolver(Path file, LinkOption... options) {
- super(file, options);
- }
-
- @Override
- public HostConfigEntry resolveEffectiveHost(String host, int port, String username) throws IOException {
- try {
- HostConfigEntryResolver delegate = Objects.requireNonNull(resolveEffectiveResolver(host, port, username), "No delegate");
- HostConfigEntry entry = delegate.resolveEffectiveHost(host, port, username);
- if (log.isDebugEnabled()) {
- log.debug("resolveEffectiveHost({}@{}:{}) => {}", username, host, port, entry);
- }
-
- return entry;
- } catch (Throwable e) {
- if (log.isDebugEnabled()) {
- log.debug("resolveEffectiveHost({}@{}:{}) failed ({}) to resolve: {}",
- username, host, port, e.getClass().getSimpleName(), e.getMessage());
- }
-
- if (log.isTraceEnabled()) {
- log.trace("resolveEffectiveHost(" + username + "@" + host + ":" + port + ") resolution failure details", e);
- }
- if (e instanceof IOException) {
- throw (IOException) e;
- } else {
- throw new IOException(e);
- }
- }
- }
-
- protected HostConfigEntryResolver resolveEffectiveResolver(String host, int port, String username) throws IOException {
- if (checkReloadRequired()) {
- delegateHolder.set(HostConfigEntryResolver.EMPTY); // start fresh
-
- Path path = getPath();
- if (exists()) {
- Collection<HostConfigEntry> entries = reloadHostConfigEntries(path, host, port, username);
- if (GenericUtils.size(entries) > 0) {
- delegateHolder.set(HostConfigEntry.toHostConfigEntryResolver(entries));
- }
- } else {
- log.info("resolveEffectiveResolver({}@{}:{}) no configuration file at {}", username, host, port, path);
- }
- }
-
- return delegateHolder.get();
- }
-
- protected List<HostConfigEntry> reloadHostConfigEntries(Path path, String host, int port, String username) throws IOException {
- List<HostConfigEntry> entries = HostConfigEntry.readHostConfigEntries(path);
- log.info("resolveEffectiveResolver({}@{}:{}) loaded {} entries from {}", username, host, port, GenericUtils.size(entries), path);
- updateReloadAttributes();
- return entries;
- }
-}
[34/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common
utilities code from sshd-core into sshd-common (new artifact)
Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PublicKeyDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PublicKeyDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PublicKeyDecoder.java
new file mode 100644
index 0000000..793965c
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/Ed25519PublicKeyDecoder.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.security.eddsa;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.util.Collections;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.impl.AbstractPublicKeyEntryDecoder;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+import net.i2p.crypto.eddsa.EdDSAPrivateKey;
+import net.i2p.crypto.eddsa.EdDSAPublicKey;
+import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
+import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class Ed25519PublicKeyDecoder extends AbstractPublicKeyEntryDecoder<EdDSAPublicKey, EdDSAPrivateKey> {
+ public static final Ed25519PublicKeyDecoder INSTANCE = new Ed25519PublicKeyDecoder();
+
+ private Ed25519PublicKeyDecoder() {
+ super(EdDSAPublicKey.class, EdDSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_ED25519)));
+ }
+
+ @Override
+ public EdDSAPublicKey clonePublicKey(EdDSAPublicKey key) throws GeneralSecurityException {
+ if (key == null) {
+ return null;
+ } else {
+ return generatePublicKey(new EdDSAPublicKeySpec(key.getA(), key.getParams()));
+ }
+ }
+
+ @Override
+ public EdDSAPrivateKey clonePrivateKey(EdDSAPrivateKey key) throws GeneralSecurityException {
+ if (key == null) {
+ return null;
+ } else {
+ return generatePrivateKey(new EdDSAPrivateKeySpec(key.getSeed(), key.getParams()));
+ }
+ }
+
+ @Override
+ public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+ return SecurityUtils.getKeyPairGenerator(SecurityUtils.EDDSA);
+ }
+
+ @Override
+ public String encodePublicKey(OutputStream s, EdDSAPublicKey key) throws IOException {
+ Objects.requireNonNull(key, "No public key provided");
+ KeyEntryResolver.encodeString(s, KeyPairProvider.SSH_ED25519);
+ byte[] seed = getSeedValue(key);
+ KeyEntryResolver.writeRLEBytes(s, seed);
+ return KeyPairProvider.SSH_ED25519;
+ }
+
+ @Override
+ public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+ return SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
+ }
+
+ @Override
+ public EdDSAPublicKey decodePublicKey(String keyType, InputStream keyData) throws IOException, GeneralSecurityException {
+ byte[] seed = KeyEntryResolver.readRLEBytes(keyData);
+ return EdDSAPublicKey.class.cast(SecurityUtils.generateEDDSAPublicKey(keyType, seed));
+ }
+
+ public static byte[] getSeedValue(EdDSAPublicKey key) {
+ // a bit of reverse-engineering on the EdDSAPublicKeySpec
+ return (key == null) ? null : key.getAbyte();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java
new file mode 100644
index 0000000..61f16e9
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderRegistrar.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.security.eddsa;
+
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.Provider;
+import java.security.Signature;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ReflectionUtils;
+import org.apache.sshd.common.util.security.AbstractSecurityProviderRegistrar;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class EdDSASecurityProviderRegistrar extends AbstractSecurityProviderRegistrar {
+ public static final String PROVIDER_CLASS = "net.i2p.crypto.eddsa.EdDSASecurityProvider";
+ // Do not define a static registrar instance to minimize class loading issues
+ private final AtomicReference<Boolean> supportHolder = new AtomicReference<>(null);
+
+ public EdDSASecurityProviderRegistrar() {
+ super(SecurityUtils.EDDSA);
+ }
+
+ @Override
+ public boolean isEnabled() {
+ if (!super.isEnabled()) {
+ return false;
+ }
+
+ // For backward compatibility
+ return this.getBooleanProperty(SecurityUtils.EDDSA_SUPPORTED_PROP, true);
+ }
+
+ @Override
+ public Provider getSecurityProvider() {
+ try {
+ return getOrCreateProvider(PROVIDER_CLASS);
+ } catch (ReflectiveOperationException t) {
+ Throwable e = GenericUtils.peelException(t);
+ log.error("getSecurityProvider({}) failed ({}) to instantiate {}: {}",
+ getName(), e.getClass().getSimpleName(), PROVIDER_CLASS, e.getMessage());
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ }
+
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public boolean isSecurityEntitySupported(Class<?> entityType, String name) {
+ if (!isSupported()) {
+ return false;
+ }
+
+ if (KeyPairGenerator.class.isAssignableFrom(entityType)
+ || KeyFactory.class.isAssignableFrom(entityType)) {
+ return Objects.compare(name, getName(), String.CASE_INSENSITIVE_ORDER) == 0;
+ } else if (Signature.class.isAssignableFrom(entityType)) {
+ return Objects.compare(SecurityUtils.CURVE_ED25519_SHA512, name, String.CASE_INSENSITIVE_ORDER) == 0;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isSupported() {
+ Boolean supported;
+ synchronized (supportHolder) {
+ supported = supportHolder.get();
+ if (supported != null) {
+ return supported.booleanValue();
+ }
+
+ ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(getClass());
+ supported = ReflectionUtils.isClassAvailable(cl, "net.i2p.crypto.eddsa.EdDSAKey");
+ supportHolder.set(supported);
+ }
+
+ return supported.booleanValue();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java
new file mode 100644
index 0000000..242f550
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.security.eddsa;
+
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+import net.i2p.crypto.eddsa.EdDSAEngine;
+import net.i2p.crypto.eddsa.EdDSAKey;
+import net.i2p.crypto.eddsa.EdDSAPrivateKey;
+import net.i2p.crypto.eddsa.EdDSAPublicKey;
+import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
+import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
+import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
+import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class EdDSASecurityProviderUtils {
+ // See EdDSANamedCurveTable
+ public static final String CURVE_ED25519_SHA512 = "Ed25519";
+
+ private EdDSASecurityProviderUtils() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ public static Class<? extends PublicKey> getEDDSAPublicKeyType() {
+ return EdDSAPublicKey.class;
+ }
+
+ public static Class<? extends PrivateKey> getEDDSAPrivateKeyType() {
+ return EdDSAPrivateKey.class;
+ }
+
+ public static int getEDDSAKeySize(Key key) {
+ return (SecurityUtils.isEDDSACurveSupported() && (key instanceof EdDSAKey)) ? 256 : -1;
+ }
+
+ public static boolean compareEDDSAPPublicKeys(PublicKey k1, PublicKey k2) {
+ if (!SecurityUtils.isEDDSACurveSupported()) {
+ return false;
+ }
+
+ if ((k1 instanceof EdDSAPublicKey) && (k2 instanceof EdDSAPublicKey)) {
+ if (Objects.equals(k1, k2)) {
+ return true;
+ } else if (k1 == null || k2 == null) {
+ return false; // both null is covered by Objects#equals
+ }
+
+ EdDSAPublicKey ed1 = (EdDSAPublicKey) k1;
+ EdDSAPublicKey ed2 = (EdDSAPublicKey) k2;
+ return Arrays.equals(ed1.getAbyte(), ed2.getAbyte())
+ && compareEDDSAKeyParams(ed1.getParams(), ed2.getParams());
+ }
+
+ return false;
+ }
+
+ public static boolean isEDDSASignatureAlgorithm(String algorithm) {
+ return EdDSAEngine.SIGNATURE_ALGORITHM.equalsIgnoreCase(algorithm);
+ }
+
+ public static EdDSAPublicKey recoverEDDSAPublicKey(PrivateKey key) throws GeneralSecurityException {
+ ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
+ if (!(key instanceof EdDSAPrivateKey)) {
+ throw new InvalidKeyException("Private key is not " + SecurityUtils.EDDSA);
+ }
+
+ EdDSAPrivateKey prvKey = (EdDSAPrivateKey) key;
+ EdDSAPublicKeySpec keySpec = new EdDSAPublicKeySpec(prvKey.getAbyte(), prvKey.getParams());
+ KeyFactory factory = SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
+ return EdDSAPublicKey.class.cast(factory.generatePublic(keySpec));
+ }
+
+ public static org.apache.sshd.common.signature.Signature getEDDSASignature() {
+ ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
+ return new SignatureEd25519();
+ }
+
+ public static boolean isEDDSAKeyFactoryAlgorithm(String algorithm) {
+ return SecurityUtils.EDDSA.equalsIgnoreCase(algorithm);
+ }
+
+ public static boolean isEDDSAKeyPairGeneratorAlgorithm(String algorithm) {
+ return SecurityUtils.EDDSA.equalsIgnoreCase(algorithm);
+ }
+
+ public static PublicKeyEntryDecoder<? extends PublicKey, ? extends PrivateKey> getEDDSAPublicKeyEntryDecoder() {
+ ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
+ return Ed25519PublicKeyDecoder.INSTANCE;
+ }
+
+ public static PrivateKeyEntryDecoder<? extends PublicKey, ? extends PrivateKey> getOpenSSHEDDSAPrivateKeyEntryDecoder() {
+ ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
+ return OpenSSHEd25519PrivateKeyEntryDecoder.INSTANCE;
+ }
+
+ public static boolean compareEDDSAPrivateKeys(PrivateKey k1, PrivateKey k2) {
+ if (!SecurityUtils.isEDDSACurveSupported()) {
+ return false;
+ }
+
+ if ((k1 instanceof EdDSAPrivateKey) && (k2 instanceof EdDSAPrivateKey)) {
+ if (Objects.equals(k1, k2)) {
+ return true;
+ } else if (k1 == null || k2 == null) {
+ return false; // both null is covered by Objects#equals
+ }
+
+ EdDSAPrivateKey ed1 = (EdDSAPrivateKey) k1;
+ EdDSAPrivateKey ed2 = (EdDSAPrivateKey) k2;
+ return Arrays.equals(ed1.getSeed(), ed2.getSeed())
+ && compareEDDSAKeyParams(ed1.getParams(), ed2.getParams());
+ }
+
+ return false;
+ }
+
+ public static boolean compareEDDSAKeyParams(EdDSAParameterSpec s1, EdDSAParameterSpec s2) {
+ if (Objects.equals(s1, s2)) {
+ return true;
+ } else if (s1 == null || s2 == null) {
+ return false; // both null is covered by Objects#equals
+ } else {
+ return Objects.equals(s1.getHashAlgorithm(), s2.getHashAlgorithm())
+ && Objects.equals(s1.getCurve(), s2.getCurve())
+ && Objects.equals(s1.getB(), s2.getB());
+ }
+ }
+
+ public static PublicKey generateEDDSAPublicKey(byte[] seed) throws GeneralSecurityException {
+ if (!SecurityUtils.isEDDSACurveSupported()) {
+ throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " not supported");
+ }
+
+ EdDSAParameterSpec params = EdDSANamedCurveTable.getByName(CURVE_ED25519_SHA512);
+ EdDSAPublicKeySpec keySpec = new EdDSAPublicKeySpec(seed, params);
+ KeyFactory factory = SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
+ return factory.generatePublic(keySpec);
+ }
+
+ public static PrivateKey generateEDDSAPrivateKey(byte[] seed) throws GeneralSecurityException {
+ if (!SecurityUtils.isEDDSACurveSupported()) {
+ throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " not supported");
+ }
+
+ EdDSAParameterSpec params = EdDSANamedCurveTable.getByName(CURVE_ED25519_SHA512);
+ EdDSAPrivateKeySpec keySpec = new EdDSAPrivateKeySpec(seed, params);
+ KeyFactory factory = SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
+ return factory.generatePrivate(keySpec);
+ }
+
+ public static <B extends Buffer> B putRawEDDSAPublicKey(B buffer, PublicKey key) {
+ ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
+ EdDSAPublicKey edKey = ValidateUtils.checkInstanceOf(key, EdDSAPublicKey.class, "Not an EDDSA public key: %s", key);
+ byte[] seed = Ed25519PublicKeyDecoder.getSeedValue(edKey);
+ ValidateUtils.checkNotNull(seed, "No seed extracted from key: %s", edKey.getA());
+ buffer.putString(KeyPairProvider.SSH_ED25519);
+ buffer.putBytes(seed);
+ return buffer;
+ }
+
+ public static <B extends Buffer> B putEDDSAKeyPair(B buffer, PublicKey pubKey, PrivateKey prvKey) {
+ ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), SecurityUtils.EDDSA + " not supported");
+ ValidateUtils.checkInstanceOf(pubKey, EdDSAPublicKey.class, "Not an EDDSA public key: %s", pubKey);
+ ValidateUtils.checkInstanceOf(prvKey, EdDSAPrivateKey.class, "Not an EDDSA private key: %s", prvKey);
+ throw new UnsupportedOperationException("Full SSHD-440 implementation N/A");
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
new file mode 100644
index 0000000..4888818
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.security.eddsa;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.KeyEntryResolver;
+import org.apache.sshd.common.config.keys.impl.AbstractPrivateKeyEntryDecoder;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+import net.i2p.crypto.eddsa.EdDSAPrivateKey;
+import net.i2p.crypto.eddsa.EdDSAPublicKey;
+import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
+import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
+import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
+import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class OpenSSHEd25519PrivateKeyEntryDecoder extends AbstractPrivateKeyEntryDecoder<EdDSAPublicKey, EdDSAPrivateKey> {
+ public static final OpenSSHEd25519PrivateKeyEntryDecoder INSTANCE = new OpenSSHEd25519PrivateKeyEntryDecoder();
+ private static final int PK_SIZE = 32;
+ private static final int SK_SIZE = 32;
+ private static final int KEYPAIR_SIZE = PK_SIZE + SK_SIZE;
+
+ public OpenSSHEd25519PrivateKeyEntryDecoder() {
+ super(EdDSAPublicKey.class, EdDSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_ED25519)));
+ }
+
+ @Override
+ public EdDSAPrivateKey decodePrivateKey(String keyType, FilePasswordProvider passwordProvider, InputStream keyData)
+ throws IOException, GeneralSecurityException {
+ if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
+ throw new InvalidKeyException("Unsupported key type: " + keyType);
+ }
+
+ if (!SecurityUtils.isEDDSACurveSupported()) {
+ throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " provider not supported");
+ }
+
+ // ed25519 bernstein naming: pk .. public key, sk .. secret key
+ // we expect to find two byte arrays with the following structure (type:size):
+ // [pk:32], [sk:32,pk:32]
+
+ byte[] pk = KeyEntryResolver.readRLEBytes(keyData);
+ byte[] keypair = KeyEntryResolver.readRLEBytes(keyData);
+
+ if (pk.length != PK_SIZE) {
+ throw new InvalidKeyException(String.format(Locale.ENGLISH, "Unexpected pk size: %s (expected %s)", pk.length, PK_SIZE));
+ }
+
+ if (keypair.length != KEYPAIR_SIZE) {
+ throw new InvalidKeyException(String.format(Locale.ENGLISH, "Unexpected keypair size: %s (expected %s)", keypair.length, KEYPAIR_SIZE));
+ }
+
+ byte[] sk = Arrays.copyOf(keypair, SK_SIZE);
+
+ // verify that the keypair contains the expected pk
+ // yes, it's stored redundant, this seems to mimic the output structure of the keypair generation interface
+ if (!Arrays.equals(pk, Arrays.copyOfRange(keypair, SK_SIZE, KEYPAIR_SIZE))) {
+ throw new InvalidKeyException("Keypair did not contain the public key.");
+ }
+
+ // create the private key
+ EdDSAParameterSpec params = EdDSANamedCurveTable.getByName(EdDSASecurityProviderUtils.CURVE_ED25519_SHA512);
+ EdDSAPrivateKey privateKey = generatePrivateKey(new EdDSAPrivateKeySpec(sk, params));
+
+ // the private key class contains the calculated public key (Abyte)
+ // pointers to the corresponding code:
+ // EdDSAPrivateKeySpec.EdDSAPrivateKeySpec(byte[], EdDSAParameterSpec): A = spec.getB().scalarMultiply(a);
+ // EdDSAPrivateKey.EdDSAPrivateKey(EdDSAPrivateKeySpec): this.Abyte = this.A.toByteArray();
+
+ // we can now verify the generated pk matches the one we read
+ if (!Arrays.equals(privateKey.getAbyte(), pk)) {
+ throw new InvalidKeyException("The provided pk does NOT match the computed pk for the given sk.");
+ }
+
+ return privateKey;
+ }
+
+ @Override
+ public String encodePrivateKey(OutputStream s, EdDSAPrivateKey key) throws IOException {
+ Objects.requireNonNull(key, "No private key provided");
+
+ // ed25519 bernstein naming: pk .. public key, sk .. secret key
+ // we are expected to write the following arrays (type:size):
+ // [pk:32], [sk:32,pk:32]
+
+ byte[] sk = key.getSeed();
+ byte[] pk = key.getAbyte();
+
+ Objects.requireNonNull(sk, "No seed");
+
+ byte[] keypair = new byte[KEYPAIR_SIZE];
+ System.arraycopy(sk, 0, keypair, 0, SK_SIZE);
+ System.arraycopy(pk, 0, keypair, SK_SIZE, PK_SIZE);
+
+ KeyEntryResolver.writeRLEBytes(s, pk);
+ KeyEntryResolver.writeRLEBytes(s, keypair);
+
+ return KeyPairProvider.SSH_ED25519;
+ }
+
+ @Override
+ public boolean isPublicKeyRecoverySupported() {
+ return true;
+ }
+
+ @Override
+ public EdDSAPublicKey recoverPublicKey(EdDSAPrivateKey prvKey) throws GeneralSecurityException {
+ return EdDSASecurityProviderUtils.recoverEDDSAPublicKey(prvKey);
+ }
+
+ @Override
+ public EdDSAPublicKey clonePublicKey(EdDSAPublicKey key) throws GeneralSecurityException {
+ if (key == null) {
+ return null;
+ } else {
+ return generatePublicKey(new EdDSAPublicKeySpec(key.getA(), key.getParams()));
+ }
+ }
+
+ @Override
+ public EdDSAPrivateKey clonePrivateKey(EdDSAPrivateKey key) throws GeneralSecurityException {
+ if (key == null) {
+ return null;
+ } else {
+ return generatePrivateKey(new EdDSAPrivateKeySpec(key.getSeed(), key.getParams()));
+ }
+ }
+
+ @Override
+ public KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException {
+ return SecurityUtils.getKeyPairGenerator(SecurityUtils.EDDSA);
+ }
+
+ @Override
+ public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+ return SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/SignatureEd25519.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/SignatureEd25519.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/SignatureEd25519.java
new file mode 100644
index 0000000..012be95
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/SignatureEd25519.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.security.eddsa;
+
+import java.util.Map;
+
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.signature.AbstractSignature;
+import org.apache.sshd.common.util.ValidateUtils;
+
+import net.i2p.crypto.eddsa.EdDSAEngine;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SignatureEd25519 extends AbstractSignature {
+ public SignatureEd25519() {
+ super(EdDSAEngine.SIGNATURE_ALGORITHM);
+ }
+
+ @Override
+ public boolean verify(byte[] sig) throws Exception {
+ byte[] data = sig;
+ Map.Entry<String, byte[]> encoding = extractEncodedSignature(data);
+ if (encoding != null) {
+ String keyType = encoding.getKey();
+ ValidateUtils.checkTrue(KeyPairProvider.SSH_ED25519.equals(keyType), "Mismatched key type: %s", keyType);
+ data = encoding.getValue();
+ }
+
+ return doVerify(data);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java
new file mode 100644
index 0000000..3b9beeb
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/CloseableExecutorService.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.threads;
+
+import java.util.concurrent.ExecutorService;
+
+import org.apache.sshd.common.Closeable;
+
+public interface CloseableExecutorService extends ExecutorService, Closeable {
+ // Nothing extra
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ExecutorServiceCarrier.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ExecutorServiceCarrier.java b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ExecutorServiceCarrier.java
new file mode 100644
index 0000000..b44bd46
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ExecutorServiceCarrier.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.util.threads;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface ExecutorServiceCarrier {
+ /**
+ * @return The {@link CloseableExecutorService} to use
+ */
+ CloseableExecutorService getExecutorService();
+
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/threads/NoCloseExecutor.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/threads/NoCloseExecutor.java b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/NoCloseExecutor.java
new file mode 100644
index 0000000..cb42805
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/NoCloseExecutor.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.threads;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.util.ValidateUtils;
+
+/**
+ * Wraps an {@link ExecutorService} as a {@link CloseableExecutorService}
+ * and avoids calling its {@code shutdown} methods when the wrapper is shut down
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class NoCloseExecutor implements CloseableExecutorService {
+ protected final ExecutorService executor;
+ protected final CloseFuture closeFuture;
+
+ public NoCloseExecutor(ExecutorService executor) {
+ this.executor = executor;
+ closeFuture = new DefaultCloseFuture(null, null);
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
+ return executor.submit(task);
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
+ return executor.submit(task, result);
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
+ return executor.submit(task);
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException {
+ ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
+ return executor.invokeAll(tasks);
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException {
+ ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
+ return executor.invokeAll(tasks, timeout, unit);
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException, ExecutionException {
+ ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
+ return executor.invokeAny(tasks);
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
+ return executor.invokeAny(tasks, timeout, unit);
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ ValidateUtils.checkState(!isShutdown(), "Executor has been shut down");
+ executor.execute(command);
+ }
+
+ @Override
+ public void shutdown() {
+ close(true);
+ }
+
+ @Override
+ public List<Runnable> shutdownNow() {
+ close(true);
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return isClosed();
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return isClosed();
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ try {
+ return closeFuture.await(timeout, unit);
+ } catch (IOException e) {
+ throw (InterruptedException) new InterruptedException().initCause(e);
+ }
+ }
+
+ @Override
+ public CloseFuture close(boolean immediately) {
+ closeFuture.setClosed();
+ return closeFuture;
+ }
+
+ @Override
+ public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ closeFuture.addListener(listener);
+ }
+
+ @Override
+ public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ closeFuture.removeListener(listener);
+ }
+
+ @Override
+ public boolean isClosed() {
+ return closeFuture.isClosed();
+ }
+
+ @Override
+ public boolean isClosing() {
+ return isClosed();
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshThreadPoolExecutor.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshThreadPoolExecutor.java b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshThreadPoolExecutor.java
new file mode 100644
index 0000000..ccaa655
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshThreadPoolExecutor.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.threads;
+
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.util.closeable.AbstractCloseable;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SshThreadPoolExecutor extends ThreadPoolExecutor implements CloseableExecutorService {
+ protected final DelegateCloseable closeable = new DelegateCloseable();
+
+ protected class DelegateCloseable extends AbstractCloseable {
+ protected DelegateCloseable() {
+ super();
+ }
+
+ @Override
+ protected CloseFuture doCloseGracefully() {
+ shutdown();
+ return closeFuture;
+ }
+
+ @Override
+ protected void doCloseImmediately() {
+ shutdownNow();
+ super.doCloseImmediately();
+ }
+
+ protected void setClosed() {
+ closeFuture.setClosed();
+ }
+ }
+
+ public SshThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
+ }
+
+ public SshThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
+ BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
+ }
+
+ public SshThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
+ BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
+ }
+
+ public SshThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
+ BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
+ }
+
+ @Override
+ protected void terminated() {
+ closeable.doCloseImmediately();
+ }
+
+ @Override
+ public void shutdown() {
+ super.shutdown();
+ }
+
+ @Override
+ public List<Runnable> shutdownNow() {
+ return super.shutdownNow();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return super.isShutdown();
+ }
+
+ @Override
+ public boolean isTerminating() {
+ return super.isTerminating();
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return super.isTerminated();
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ return super.awaitTermination(timeout, unit);
+ }
+
+ @Override
+ public CloseFuture close(boolean immediately) {
+ return closeable.close(immediately);
+ }
+
+ @Override
+ public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ closeable.addCloseFutureListener(listener);
+ }
+
+ @Override
+ public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+ closeable.removeCloseFutureListener(listener);
+ }
+
+ @Override
+ public boolean isClosed() {
+ return closeable.isClosed();
+ }
+
+ @Override
+ public boolean isClosing() {
+ return closeable.isClosing();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java
new file mode 100644
index 0000000..5dc0c7b
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/SshdThreadFactory.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.threads;
+
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+/**
+ * Default {@link ThreadFactory} used by {@link ThreadUtils} to create
+ * thread pools if user did provide one
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SshdThreadFactory extends AbstractLoggingBean implements ThreadFactory {
+ private final ThreadGroup group;
+ private final AtomicInteger threadNumber = new AtomicInteger(1);
+ private final String namePrefix;
+
+ public SshdThreadFactory(String name) {
+ SecurityManager s = System.getSecurityManager();
+ group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
+ String effectiveName = name.replace(' ', '-');
+ namePrefix = "sshd-" + effectiveName + "-thread-";
+ }
+
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread t;
+ try {
+ // see SSHD-668
+ if (System.getSecurityManager() != null) {
+ t = AccessController.doPrivileged((PrivilegedExceptionAction<Thread>) () ->
+ new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0));
+ } else {
+ t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
+ }
+ } catch (PrivilegedActionException e) {
+ Exception err = e.getException();
+ if (err instanceof RuntimeException) {
+ throw (RuntimeException) err;
+ } else {
+ throw new RuntimeException(err);
+ }
+ }
+
+ if (!t.isDaemon()) {
+ t.setDaemon(true);
+ }
+ if (t.getPriority() != Thread.NORM_PRIORITY) {
+ t.setPriority(Thread.NORM_PRIORITY);
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("newThread({})[{}] runnable={}", group, t.getName(), r);
+ }
+ return t;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java
new file mode 100644
index 0000000..c803389
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/threads/ThreadUtils.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.threads;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Utility class for thread pools.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class ThreadUtils {
+ private ThreadUtils() {
+ throw new UnsupportedOperationException("No instance");
+ }
+
+ /**
+ * Wraps an {@link CloseableExecutorService} in such a way as to "protect"
+ * it for calls to the {@link CloseableExecutorService#shutdown()} or
+ * {@link CloseableExecutorService#shutdownNow()}. All other calls are delegated as-is
+ * to the original service. <B>Note:</B> the exposed wrapped proxy will
+ * answer correctly the {@link CloseableExecutorService#isShutdown()} query if indeed
+ * one of the {@code shutdown} methods was invoked.
+ *
+ * @param executorService The original service - ignored if {@code null}
+ * @param shutdownOnExit If {@code true} then it is OK to shutdown the executor
+ * so no wrapping takes place.
+ * @return Either the original service or a wrapped one - depending on the
+ * value of the <tt>shutdownOnExit</tt> parameter
+ */
+ public static CloseableExecutorService protectExecutorServiceShutdown(CloseableExecutorService executorService, boolean shutdownOnExit) {
+ if (executorService == null || shutdownOnExit || executorService instanceof NoCloseExecutor) {
+ return executorService;
+ } else {
+ return new NoCloseExecutor(executorService);
+ }
+ }
+
+ public static CloseableExecutorService noClose(CloseableExecutorService executorService) {
+ return protectExecutorServiceShutdown(executorService, false);
+ }
+
+ public static ClassLoader resolveDefaultClassLoader(Object anchor) {
+ return resolveDefaultClassLoader(anchor == null ? null : anchor.getClass());
+ }
+
+ public static Iterable<ClassLoader> resolveDefaultClassLoaders(Object anchor) {
+ return resolveDefaultClassLoaders(anchor == null ? null : anchor.getClass());
+ }
+
+ public static <T> T createDefaultInstance(Class<?> anchor, Class<T> targetType, String className)
+ throws ReflectiveOperationException {
+ return createDefaultInstance(resolveDefaultClassLoaders(anchor), targetType, className);
+ }
+
+ public static <T> T createDefaultInstance(ClassLoader cl, Class<T> targetType, String className)
+ throws ReflectiveOperationException {
+ Class<?> instanceType = cl.loadClass(className);
+ Object instance = instanceType.newInstance();
+ return targetType.cast(instance);
+ }
+
+ public static <T> T createDefaultInstance(Iterable<ClassLoader> cls, Class<T> targetType, String className)
+ throws ReflectiveOperationException {
+ for (ClassLoader cl : cls) {
+ try {
+ return createDefaultInstance(cl, targetType, className);
+ } catch (ClassNotFoundException e) {
+ // Ignore
+ }
+ }
+ throw new ClassNotFoundException(className);
+ }
+
+ /**
+ * <P>Attempts to find the most suitable {@link ClassLoader} as follows:</P>
+ * <UL>
+ * <LI><P>
+ * Check the {@link Thread#getContextClassLoader()} value
+ * </P></LI>
+ *
+ * <LI><P>
+ * If no thread context class loader then check the anchor
+ * class (if given) for its class loader
+ * </P></LI>
+ *
+ * <LI><P>
+ * If still no loader available, then use {@link ClassLoader#getSystemClassLoader()}
+ * </P></LI>
+ * </UL>
+ *
+ * @param anchor The anchor {@link Class} to use if no current thread
+ * - ignored if {@code null}
+ * context class loader
+ * @return The resolver {@link ClassLoader}
+ */
+ public static ClassLoader resolveDefaultClassLoader(Class<?> anchor) {
+ Thread thread = Thread.currentThread();
+ ClassLoader cl = thread.getContextClassLoader();
+ if (cl != null) {
+ return cl;
+ }
+
+ if (anchor != null) {
+ cl = anchor.getClassLoader();
+ }
+
+ if (cl == null) { // can happen for core Java classes
+ cl = ClassLoader.getSystemClassLoader();
+ }
+
+ return cl;
+ }
+
+ public static Iterable<ClassLoader> resolveDefaultClassLoaders(Class<?> anchor) {
+ Set<ClassLoader> cls = new LinkedHashSet<>();
+ Thread thread = Thread.currentThread();
+ ClassLoader cl = thread.getContextClassLoader();
+ if (cl != null) {
+ cls.add(cl);
+ }
+ if (anchor != null) {
+ cls.add(anchor.getClassLoader());
+ }
+ cls.add(ClassLoader.getSystemClassLoader());
+ return cls;
+ }
+
+ public static CloseableExecutorService newFixedThreadPoolIf(CloseableExecutorService executorService, String poolName, int nThreads) {
+ return executorService == null ? newFixedThreadPool(poolName, nThreads) : executorService;
+ }
+
+ public static CloseableExecutorService newFixedThreadPool(String poolName, int nThreads) {
+ return new SshThreadPoolExecutor(
+ nThreads, nThreads,
+ 0L, TimeUnit.MILLISECONDS, // TODO make this configurable
+ new LinkedBlockingQueue<>(),
+ new SshdThreadFactory(poolName),
+ new ThreadPoolExecutor.CallerRunsPolicy());
+ }
+
+ public static CloseableExecutorService newCachedThreadPoolIf(CloseableExecutorService executorService, String poolName) {
+ return executorService == null ? newCachedThreadPool(poolName) : executorService;
+ }
+
+ public static CloseableExecutorService newCachedThreadPool(String poolName) {
+ return new SshThreadPoolExecutor(
+ 0, Integer.MAX_VALUE, // TODO make this configurable
+ 60L, TimeUnit.SECONDS, // TODO make this configurable
+ new SynchronousQueue<>(),
+ new SshdThreadFactory(poolName),
+ new ThreadPoolExecutor.CallerRunsPolicy());
+ }
+
+ public static ScheduledExecutorService newSingleThreadScheduledExecutor(String poolName) {
+ return new ScheduledThreadPoolExecutor(1, new SshdThreadFactory(poolName));
+ }
+
+ public static CloseableExecutorService newSingleThreadExecutor(String poolName) {
+ return newFixedThreadPool(poolName, 1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java b/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
new file mode 100644
index 0000000..9131f99
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/AbstractGeneratorHostKeyProvider.java
@@ -0,0 +1,293 @@
+/*
+ * 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.server.keyprovider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.BuiltinIdentities;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+
+/**
+ * Holds a <U>single</U> {@link KeyPair} which is generated the 1st time
+ * {@link #loadKeys()} is called. If there is a file backing it up and the
+ * file exists, the key is loaded from it. Otherwise a new key pair is
+ * generated and saved (provided a path is configured and {@link #isOverwriteAllowed()}
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairProvider {
+ public static final String DEFAULT_ALGORITHM = KeyUtils.RSA_ALGORITHM;
+ public static final boolean DEFAULT_ALLOWED_TO_OVERWRITE = true;
+
+ private final AtomicReference<KeyPair> keyPairHolder = new AtomicReference<>();
+
+ private Path path;
+ private String algorithm = DEFAULT_ALGORITHM;
+ private int keySize;
+ private AlgorithmParameterSpec keySpec;
+ private boolean overwriteAllowed = DEFAULT_ALLOWED_TO_OVERWRITE;
+
+ protected AbstractGeneratorHostKeyProvider() {
+ super();
+ }
+
+ public Path getPath() {
+ return path;
+ }
+
+ public void setFile(File file) {
+ setPath((file == null) ? null : file.toPath());
+ }
+
+ public void setPath(Path path) {
+ this.path = (path == null) ? null : path.toAbsolutePath();
+ }
+
+ public String getAlgorithm() {
+ return algorithm;
+ }
+
+ public void setAlgorithm(String algorithm) {
+ this.algorithm = algorithm;
+ }
+
+ public int getKeySize() {
+ return keySize;
+ }
+
+ public void setKeySize(int keySize) {
+ this.keySize = keySize;
+ }
+
+ public AlgorithmParameterSpec getKeySpec() {
+ return keySpec;
+ }
+
+ public void setKeySpec(AlgorithmParameterSpec keySpec) {
+ this.keySpec = keySpec;
+ }
+
+ public boolean isOverwriteAllowed() {
+ return overwriteAllowed;
+ }
+
+ public void setOverwriteAllowed(boolean overwriteAllowed) {
+ this.overwriteAllowed = overwriteAllowed;
+ }
+
+ public void clearLoadedKeys() {
+ KeyPair kp;
+ synchronized (keyPairHolder) {
+ kp = keyPairHolder.getAndSet(null);
+ }
+
+ if ((kp != null) & log.isDebugEnabled()) {
+ PublicKey key = kp.getPublic();
+ log.debug("clearLoadedKeys({}) removed key={}-{}",
+ getPath(), KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+ }
+ }
+
+ @Override // co-variant return
+ public synchronized List<KeyPair> loadKeys() {
+ Path keyPath = getPath();
+ KeyPair kp;
+ synchronized (keyPairHolder) {
+ kp = keyPairHolder.get();
+ if (kp == null) {
+ try {
+ kp = resolveKeyPair(keyPath);
+ if (kp != null) {
+ keyPairHolder.set(kp);
+ }
+ } catch (Throwable t) {
+ log.warn("loadKeys({}) Failed ({}) to resolve: {}",
+ keyPath, t.getClass().getSimpleName(), t.getMessage());
+ if (log.isDebugEnabled()) {
+ log.debug("loadKeys(" + keyPath + ") resolution failure details", t);
+ }
+ }
+ }
+ }
+
+ if (kp == null) {
+ return Collections.emptyList();
+ } else {
+ return Collections.singletonList(kp);
+ }
+ }
+
+ protected KeyPair resolveKeyPair(Path keyPath) throws IOException, GeneralSecurityException {
+ String alg = getAlgorithm();
+ KeyPair kp;
+ if (keyPath != null) {
+ try {
+ kp = loadFromFile(alg, keyPath);
+ if (kp != null) {
+ return kp;
+ }
+ } catch (Throwable e) {
+ log.warn("resolveKeyPair({}) Failed ({}) to load: {}",
+ keyPath, e.getClass().getSimpleName(), e.getMessage());
+ if (log.isDebugEnabled()) {
+ log.debug("resolveKeyPair(" + keyPath + ") load failure details", e);
+ }
+ }
+ }
+
+ // either no file specified or no key in file
+ try {
+ kp = generateKeyPair(alg);
+ if (kp == null) {
+ return null;
+ }
+
+ if (log.isDebugEnabled()) {
+ PublicKey key = kp.getPublic();
+ log.debug("resolveKeyPair({}) generated {} key={}-{}",
+ keyPath, alg, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+ }
+ } catch (Throwable e) {
+ log.warn("resolveKeyPair({})[{}] Failed ({}) to generate {} key-pair: {}",
+ keyPath, alg, e.getClass().getSimpleName(), alg, e.getMessage());
+ if (log.isDebugEnabled()) {
+ log.debug("resolveKeyPair(" + keyPath + ")[" + alg + "] key-pair generation failure details", e);
+ }
+
+ return null;
+ }
+
+ if (keyPath != null) {
+ try {
+ writeKeyPair(kp, keyPath);
+ } catch (Throwable e) {
+ log.warn("resolveKeyPair({})[{}] Failed ({}) to write {} key: {}",
+ alg, keyPath, e.getClass().getSimpleName(), alg, e.getMessage());
+ if (log.isDebugEnabled()) {
+ log.debug("resolveKeyPair(" + keyPath + ")[" + alg + "] write failure details", e);
+ }
+ }
+ }
+
+ return kp;
+ }
+
+ protected KeyPair loadFromFile(String alg, Path keyPath) throws IOException, GeneralSecurityException {
+ LinkOption[] options = IoUtils.getLinkOptions(true);
+ if ((!Files.exists(keyPath, options)) || (!Files.isRegularFile(keyPath, options))) {
+ return null;
+ }
+
+ KeyPair kp = readKeyPair(keyPath, IoUtils.EMPTY_OPEN_OPTIONS);
+ if (kp == null) {
+ return null;
+ }
+
+ PublicKey key = kp.getPublic();
+ String keyAlgorithm = key.getAlgorithm();
+ if (BuiltinIdentities.Constants.ECDSA.equalsIgnoreCase(keyAlgorithm)) {
+ keyAlgorithm = KeyUtils.EC_ALGORITHM;
+ } else if (BuiltinIdentities.Constants.ED25519.equalsIgnoreCase(keyAlgorithm)) {
+ keyAlgorithm = SecurityUtils.EDDSA;
+ }
+
+ if (Objects.equals(alg, keyAlgorithm)) {
+ if (log.isDebugEnabled()) {
+ log.debug("resolveKeyPair({}) loaded key={}-{}",
+ keyPath, KeyUtils.getKeyType(key), KeyUtils.getFingerPrint(key));
+ }
+ return kp;
+ }
+
+ // Not same algorithm - start again
+ if (log.isDebugEnabled()) {
+ log.debug("resolveKeyPair({}) mismatched loaded key algorithm: expected={}, loaded={}",
+ keyPath, alg, keyAlgorithm);
+ }
+ Files.deleteIfExists(keyPath);
+ return null;
+ }
+
+ protected KeyPair readKeyPair(Path keyPath, OpenOption... options) throws IOException, GeneralSecurityException {
+ try (InputStream inputStream = Files.newInputStream(keyPath, options)) {
+ return doReadKeyPair(keyPath.toString(), inputStream);
+ }
+ }
+
+ protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException {
+ return SecurityUtils.loadKeyPairIdentity(resourceKey, inputStream, null);
+ }
+
+ protected void writeKeyPair(KeyPair kp, Path keyPath, OpenOption... options) throws IOException, GeneralSecurityException {
+ if ((!Files.exists(keyPath)) || isOverwriteAllowed()) {
+ try (OutputStream os = Files.newOutputStream(keyPath, options)) {
+ doWriteKeyPair(keyPath.toString(), kp, os);
+ } catch (Throwable e) {
+ log.warn("writeKeyPair({}) failed ({}) to write key {}: {}",
+ keyPath, e.getClass().getSimpleName(), e.getMessage());
+ if (log.isDebugEnabled()) {
+ log.debug("writeKeyPair(" + keyPath + ") write failure details", e);
+ }
+ }
+ } else {
+ log.error("Overwriting key ({}) is disabled: using throwaway {}: {}",
+ keyPath, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint((kp == null) ? null : kp.getPublic()));
+ }
+ }
+
+ protected abstract void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException;
+
+ protected KeyPair generateKeyPair(String algorithm) throws GeneralSecurityException {
+ KeyPairGenerator generator = SecurityUtils.getKeyPairGenerator(algorithm);
+ if (keySpec != null) {
+ generator.initialize(keySpec);
+ log.info("generateKeyPair(" + algorithm + ") generating host key - spec=" + keySpec.getClass().getSimpleName());
+ } else if (keySize != 0) {
+ generator.initialize(keySize);
+ log.info("generateKeyPair(" + algorithm + ") generating host key - size=" + keySize);
+ } else if (KeyUtils.EC_ALGORITHM.equals(algorithm)) {
+ // If left to our own devices choose the biggest key size possible
+ int numCurves = ECCurves.SORTED_KEY_SIZE.size();
+ ECCurves curve = ECCurves.SORTED_KEY_SIZE.get(numCurves - 1);
+ generator.initialize(curve.getParameters());
+ }
+
+ return generator.generateKeyPair();
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java b/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
new file mode 100644
index 0000000..3bccde8
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/server/keyprovider/SimpleGeneratorHostKeyProvider.java
@@ -0,0 +1,67 @@
+/*
+ * 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.server.keyprovider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.spec.InvalidKeySpecException;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SimpleGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProvider {
+ public SimpleGeneratorHostKeyProvider() {
+ super();
+ }
+
+ public SimpleGeneratorHostKeyProvider(File file) {
+ this((file == null) ? null : file.toPath());
+ }
+
+ public SimpleGeneratorHostKeyProvider(Path path) {
+ setPath(path);
+ }
+
+ @Override
+ protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException {
+ try (ObjectInputStream r = new ObjectInputStream(inputStream)) {
+ try {
+ return (KeyPair) r.readObject();
+ } catch (ClassNotFoundException e) {
+ throw new InvalidKeySpecException("Missing classes: " + e.getMessage(), e);
+ }
+ }
+ }
+
+ @Override
+ protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
+ try (ObjectOutputStream w = new ObjectOutputStream(outputStream)) {
+ w.writeObject(kp);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/auth/password/PasswordIdentityProviderTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/auth/password/PasswordIdentityProviderTest.java b/sshd-common/src/test/java/org/apache/sshd/client/auth/password/PasswordIdentityProviderTest.java
new file mode 100644
index 0000000..165805d
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/client/auth/password/PasswordIdentityProviderTest.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.client.auth.password;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class PasswordIdentityProviderTest extends JUnitTestSupport {
+ public PasswordIdentityProviderTest() {
+ super();
+ }
+
+ @Test
+ public void testMultiProvider() {
+ String[][] values = {
+ {getClass().getSimpleName(), getCurrentTestName()},
+ {new Date(System.currentTimeMillis()).toString()},
+ {getClass().getPackage().getName()}
+ };
+ List<String> expected = new ArrayList<>();
+ Collection<PasswordIdentityProvider> providers = new LinkedList<>();
+ for (String[] va : values) {
+ Collection<String> passwords = Arrays.asList(va);
+ expected.addAll(passwords);
+
+ PasswordIdentityProvider p = PasswordIdentityProvider.wrapPasswords(passwords);
+ assertProviderContents("Wrapped", p, passwords);
+ providers.add(p);
+ }
+
+ PasswordIdentityProvider p = PasswordIdentityProvider.multiProvider(providers);
+ assertProviderContents("Multi", p, expected);
+ }
+
+ private static void assertProviderContents(String message, PasswordIdentityProvider p, Iterable<String> expected) {
+ assertNotNull(message + ": no provider", p);
+ assertEquals(message, expected, p.loadPasswords());
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolverTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolverTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolverTest.java
new file mode 100644
index 0000000..741ba7b
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/ConfigFileHostEntryResolverTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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.client.config.hosts;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class ConfigFileHostEntryResolverTest extends JUnitTestSupport {
+ public ConfigFileHostEntryResolverTest() {
+ super();
+ }
+
+ @Test
+ public void testConfigFileReload() throws IOException {
+ Path dir = getTempTargetRelativeFile(getClass().getSimpleName());
+ AtomicInteger reloadCount = new AtomicInteger();
+ ConfigFileHostEntryResolver resolver = new ConfigFileHostEntryResolver(assertHierarchyTargetFolderExists(dir).resolve(getCurrentTestName() + ".config.txt")) {
+ @Override
+ protected List<HostConfigEntry> reloadHostConfigEntries(Path path, String host, int port, String username)
+ throws IOException {
+ reloadCount.incrementAndGet();
+ return super.reloadHostConfigEntries(path, host, port, username);
+ }
+ };
+ Path path = resolver.getPath();
+
+ HostConfigEntry expected = new HostConfigEntry(getCurrentTestName(), getCurrentTestName(), 7365, getCurrentTestName());
+ testConfigFileReload("Non-existing", path, reloadCount, null, resolver, expected, null);
+ testConfigFileReload("Empty", path, reloadCount, Collections.emptyList(), resolver, expected, null);
+ testConfigFileReload("Global", path, reloadCount,
+ Collections.singletonList(new HostConfigEntry(HostPatternsHolder.ALL_HOSTS_PATTERN, expected.getHost(), expected.getPort(), expected.getUsername())),
+ resolver, expected, expected);
+ testConfigFileReload("Wildcard", path, reloadCount,
+ Arrays.asList(
+ new HostConfigEntry(
+ HostPatternsHolder.ALL_HOSTS_PATTERN,
+ getClass().getSimpleName(),
+ 1234,
+ getClass().getSimpleName()),
+ new HostConfigEntry(
+ expected.getHost() + Character.toString(HostPatternsHolder.WILDCARD_PATTERN),
+ expected.getHost(),
+ expected.getPort(),
+ expected.getUsername())),
+ resolver, expected, expected);
+ testConfigFileReload("Specific", path, reloadCount,
+ Arrays.asList(
+ new HostConfigEntry(
+ HostPatternsHolder.ALL_HOSTS_PATTERN,
+ getClass().getSimpleName(),
+ 1234,
+ getClass().getSimpleName()),
+ new HostConfigEntry(
+ getClass().getSimpleName() + Character.toString(HostPatternsHolder.WILDCARD_PATTERN),
+ getClass().getSimpleName(),
+ 1234,
+ getClass().getSimpleName()),
+ expected),
+ resolver, expected, expected);
+ }
+
+ private static void testConfigFileReload(
+ String phase, Path path, AtomicInteger reloadCount,
+ Collection<? extends HostConfigEntry> entries,
+ HostConfigEntryResolver resolver,
+ HostConfigEntry query,
+ HostConfigEntry expected)
+ throws IOException {
+ if (entries == null) {
+ if (Files.exists(path)) {
+ Files.delete(path);
+ }
+ } else {
+ HostConfigEntry.writeHostConfigEntries(path, entries, IoUtils.EMPTY_OPEN_OPTIONS);
+ }
+
+ reloadCount.set(0);
+
+ for (int index = 1; index < Byte.SIZE; index++) {
+ HostConfigEntry actual =
+ resolver.resolveEffectiveHost(query.getHostName(), query.getPort(), query.getUsername());
+
+ if (entries == null) {
+ assertEquals(phase + "[" + index + "]: mismatched reload count", 0, reloadCount.get());
+ } else {
+ assertEquals(phase + "[" + index + "]: mismatched reload count", 1, reloadCount.get());
+ }
+
+ if (expected == null) {
+ assertNull(phase + "[" + index + "]: Unexpected success for " + query, actual);
+ } else {
+ assertNotNull(phase + "[" + index + "]: No result for " + query, actual);
+ assertNotSame(phase + "[" + index + "]: No cloned result for " + query, expected, actual);
+ assertEquals(phase + "[" + index + "]: Mismatched host for " + query,
+ expected.getHostName(), actual.getHostName());
+ assertEquals(phase + "[" + index + "]: Mismatched port for " + query,
+ expected.getPort(), actual.getPort());
+ assertEquals(phase + "[" + index + "]: Mismatched user for " + query,
+ expected.getUsername(), actual.getUsername());
+ }
+ }
+ }
+}