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/11/22 05:05:28 UTC
[5/7] mina-sshd git commit: [SSHD-757] Added some hooks to allow
usage of OpenPGP keys inside authorized_keys files
[SSHD-757] Added some hooks to allow usage of OpenPGP keys inside authorized_keys files
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/2f92fe5d
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/2f92fe5d
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/2f92fe5d
Branch: refs/heads/master
Commit: 2f92fe5d219bb688f7e43ca28fb7fb81725aaaca
Parents: fc7a8e7
Author: Lyor Goldstein <lg...@apache.org>
Authored: Mon Nov 19 16:29:38 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Thu Nov 22 07:05:16 2018 +0200
----------------------------------------------------------------------
CHANGES.md | 5 +
README.md | 14 ++
.../client/config/hosts/HostConfigEntry.java | 7 -
.../client/config/hosts/KnownHostEntry.java | 7 -
.../sshd/common/AlgorithmNameProvider.java | 27 +++
.../apache/sshd/common/cipher/BaseCipher.java | 11 +
.../sshd/common/cipher/BaseRC4Cipher.java | 6 +-
.../sshd/common/cipher/BuiltinCiphers.java | 8 +-
.../sshd/common/cipher/CipherInformation.java | 12 +-
.../apache/sshd/common/cipher/CipherNone.java | 5 +
.../org/apache/sshd/common/cipher/ECCurves.java | 14 +-
.../common/config/ConfigFileReaderSupport.java | 7 -
.../common/config/keys/AuthorizedKeyEntry.java | 71 +++---
.../sshd/common/config/keys/Identity.java | 11 +-
.../sshd/common/config/keys/KeyRandomArt.java | 10 +-
.../sshd/common/config/keys/KeyUtils.java | 113 +++++++++-
.../sshd/common/config/keys/PublicKeyEntry.java | 222 ++++++++++++++++---
.../config/keys/PublicKeyEntryDataResolver.java | 71 ++++++
.../config/keys/PublicKeyEntryDecoder.java | 19 +-
.../loader/pem/KeyPairPEMResourceParser.java | 10 +-
.../sshd/common/digest/DigestInformation.java | 12 +-
.../common/keyprovider/KeySizeIndicator.java | 30 +++
.../common/keyprovider/KeyTypeIndicator.java | 31 +++
.../apache/sshd/common/mac/MacInformation.java | 11 +-
.../apache/sshd/common/signature/Signature.java | 13 +-
.../sshd/common/util/buffer/BufferUtils.java | 34 ++-
.../AbstractGeneratorHostKeyProvider.java | 8 +-
.../pubkey/KeySetPublickeyAuthenticator.java | 17 +-
.../auth/pubkey/PublickeyAuthenticator.java | 17 +-
.../keys/AuthorizedKeysAuthenticator.java | 28 ++-
.../DefaultAuthorizedKeysAuthenticator.java | 5 +-
.../config/keys/AuthorizedKeyEntryTest.java | 8 +-
.../config/keys/AuthorizedKeysTestSupport.java | 9 +-
.../signature/SignatureFactoriesTest.java | 26 ++-
.../keys/AuthorizedKeysAuthenticatorTest.java | 15 +-
.../openpgp/PGPPublicKeyEntryDataResolver.java | 106 +++++++++
.../openpgp/PGPPublicKeyEntryDecoder.java | 87 ++++++++
.../config/keys/loader/openpgp/PGPUtils.java | 115 ++++++++++
.../openpgp/PGPKeyPairResourceParserTest.java | 6 +-
.../openpgp/PGPUtilsKeyFingerprintTest.java | 174 +++++++++++++++
40 files changed, 1198 insertions(+), 204 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 5074ade..db1e874 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -66,8 +66,13 @@ accept also an `AttributeRepository` connection context argument (propagated fro
* The various `ClientIdentitiesWatcher`(s) use a type-safe `ClientIdentityLoaderHolder` and
`FilePasswordProviderHolder` instead of the generic `Supplier` definition.
+* Removed API(s) that used string file paths to create `FileInputStream`-s - using only `java.nio.file.Path`-s
+
## Behavioral changes and enhancements
+* [SSHD-757](https://issues.apache.org/jira/browse/SSHD-757) - Added hooks and some initial code to allow (limited) usage
+of [OpenPGP](https://www.openpgp.org/) key files - e.g. in `authorized_keys` files or as client identities.
+
* [SSHD-849](https://issues.apache.org/jira/browse/SSHD-849) - Data forwarding code makes sure all
pending packets have been sent to the peer channel when closing the tunnel gracefully.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 3b291a0..811911d 100644
--- a/README.md
+++ b/README.md
@@ -1932,6 +1932,20 @@ This code relies on the [jpgpgj](https://github.com/justinludwig/jpgpj) support
(which in turn automatically uses _Bouncycastle_ - so if one does not want _Bouncycastle_ one cannot use this module).
+#### Using OpenPGP authorized keys entries
+
+In order to be able to read `authorized_keys` files that may contain _OpenPGP_ keys references, one needs to register
+the relevant `PublicKeyEntryDataResolver`-s. This is done by calling `PGPPublicKeyEntryDataResolver#registerDefaultKeyEntryDataResolvers`
+once during the _main_ code setup. This will enable the code to read authorized keys entries having the format
+specified in the [OpenSSH PGP configuration](https://www.red-bean.com/~nemo/openssh-gpg/):
+
+```
+ pgp-sign-dss 87C36E60187451050A4F26B134824FC95C781A18 with-comment
+ pgp-sign-rsa 87C36E60187451050A4F26B134824FC95C781A18
+```
+
+Where the key data following the key type specification is the fingerprint value of the referenced key.
+
## Useful extra components in _sshd-contrib_
* `InteractivePasswordIdentityProvider` - helps implement a `PasswordIdentityProvider` by delegating calls
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index c2d3bcb..f94f775 100644
--- 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
@@ -20,7 +20,6 @@ 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;
@@ -790,12 +789,6 @@ public class HostConfigEntry extends HostPatternsHolder implements MutableUserHo
}
}
- 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);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index 4a8e9ca..bfb444f 100644
--- 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
@@ -20,7 +20,6 @@
package org.apache.sshd.client.config.hosts;
import java.io.BufferedReader;
-import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -155,12 +154,6 @@ public class KnownHostEntry extends HostPatternsHolder {
}
}
- 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);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/sshd-common/src/main/java/org/apache/sshd/common/AlgorithmNameProvider.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/AlgorithmNameProvider.java b/sshd-common/src/main/java/org/apache/sshd/common/AlgorithmNameProvider.java
new file mode 100644
index 0000000..87d4959
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/AlgorithmNameProvider.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;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface AlgorithmNameProvider {
+ String getAlgorithm();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index c3d9426..306b72d 100644
--- 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
@@ -36,13 +36,19 @@ public class BaseCipher implements Cipher {
private final int ivsize;
private final int bsize;
private final String algorithm;
+ private final int keySize;
private final String transformation;
private String s;
public BaseCipher(int ivsize, int bsize, String algorithm, String transformation) {
+ this(ivsize, bsize, algorithm, bsize * Byte.SIZE, transformation);
+ }
+
+ public BaseCipher(int ivsize, int bsize, String algorithm, int keySize, String transformation) {
this.ivsize = ivsize;
this.bsize = bsize;
this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No algorithm");
+ this.keySize = keySize;
this.transformation = ValidateUtils.checkNotNullAndNotEmpty(transformation, "No transformation");
}
@@ -52,6 +58,11 @@ public class BaseCipher implements Cipher {
}
@Override
+ public int getKeySize() {
+ return keySize;
+ }
+
+ @Override
public String getTransformation() {
return transformation;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index 2e3fc1e..5185fe2 100644
--- 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
@@ -30,7 +30,11 @@ public class BaseRC4Cipher extends BaseCipher {
public static final int SKIP_SIZE = 1536;
public BaseRC4Cipher(int ivsize, int bsize) {
- super(ivsize, bsize, "ARCFOUR", "RC4");
+ this(ivsize, bsize, bsize * Byte.SIZE);
+ }
+
+ public BaseRC4Cipher(int ivsize, int bsize, int keySize) {
+ super(ivsize, bsize, "ARCFOUR", keySize, "RC4");
}
@Override
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index 8884e15..55925f2 100644
--- 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
@@ -67,7 +67,7 @@ public enum BuiltinCiphers implements CipherFactory {
arcfour256(Constants.ARCFOUR256, 8, 32, "ARCFOUR", "RC4") {
@Override
public Cipher create() {
- return new BaseRC4Cipher(getIVSize(), getBlockSize());
+ return new BaseRC4Cipher(getIVSize(), getBlockSize(), getKeySize());
}
},
blowfishcbc(Constants.BLOWFISH_CBC, 8, 16, "Blowfish", "Blowfish/CBC/NoPadding"),
@@ -123,9 +123,7 @@ public enum BuiltinCiphers implements CipherFactory {
return supported;
}
- /**
- * @return The key size (in bits) for the cipher
- */
+ @Override
public int getKeySize() {
return keysize;
}
@@ -152,7 +150,7 @@ public enum BuiltinCiphers implements CipherFactory {
@Override
public Cipher create() {
- return new BaseCipher(getIVSize(), getBlockSize(), getAlgorithm(), getTransformation());
+ return new BaseCipher(getIVSize(), getBlockSize(), getAlgorithm(), getKeySize(), getTransformation());
}
/**
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index f17fd16..96e7d41 100644
--- 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
@@ -19,15 +19,15 @@
package org.apache.sshd.common.cipher;
+import org.apache.sshd.common.AlgorithmNameProvider;
+import org.apache.sshd.common.keyprovider.KeySizeIndicator;
+
/**
+ * The reported algorithm name refers to the cipher base name - e.g., "AES", "ARCFOUR", etc.
+ *
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface CipherInformation {
- /**
- * @return The cipher's algorithm
- */
- String getAlgorithm();
-
+public interface CipherInformation extends AlgorithmNameProvider, KeySizeIndicator {
/**
* @return The actual transformation used - e.g., AES/CBC/NoPadding
*/
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index 15b6e9f..b0a21d4 100644
--- 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
@@ -37,6 +37,11 @@ public class CipherNone implements Cipher {
}
@Override
+ public int getKeySize() {
+ return 0;
+ }
+
+ @Override
public String getTransformation() {
return "none";
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index cb8cd0d..3ccb671 100644
--- 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
@@ -45,6 +45,8 @@ 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.keyprovider.KeySizeIndicator;
+import org.apache.sshd.common.keyprovider.KeyTypeIndicator;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.NumberUtils;
import org.apache.sshd.common.util.ValidateUtils;
@@ -55,7 +57,7 @@ import org.apache.sshd.common.util.security.SecurityUtils;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public enum ECCurves implements NamedResource, OptionalFeature {
+public enum ECCurves implements KeyTypeIndicator, KeySizeIndicator, NamedResource, OptionalFeature {
nistp256(Constants.NISTP256, new int[]{1, 2, 840, 10045, 3, 1, 7},
new ECParameterSpec(
new EllipticCurve(
@@ -153,7 +155,7 @@ public enum ECCurves implements NamedResource, OptionalFeature {
this.digestFactory = Objects.requireNonNull(digestFactory, "No digestFactory");
}
- @Override // The curve name
+ @Override // The curve's standard name
public final String getName() {
return name;
}
@@ -166,9 +168,7 @@ public enum ECCurves implements NamedResource, OptionalFeature {
return oidValue;
}
- /**
- * @return The standard key type used to represent this curve
- */
+ @Override
public final String getKeyType() {
return keyType;
}
@@ -182,9 +182,7 @@ public enum ECCurves implements NamedResource, OptionalFeature {
return params;
}
- /**
- * @return The size (in bits) of the key
- */
+ @Override
public final int getKeySize() {
return keySize;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index b6cab4f..f61d74e 100644
--- 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
@@ -20,7 +20,6 @@
package org.apache.sshd.common.config;
import java.io.BufferedReader;
-import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -123,12 +122,6 @@ public final class ConfigFileReaderSupport {
}
}
- 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);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index eb7e1fb..2e60968 100644
--- 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
@@ -20,7 +20,6 @@
package org.apache.sshd.common.config.keys;
import java.io.BufferedReader;
-import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -90,7 +89,8 @@ public class AuthorizedKeyEntry extends PublicKeyEntry {
}
@Override
- public PublicKey appendPublicKey(Appendable sb, PublicKeyEntryResolver fallbackResolver) throws IOException, GeneralSecurityException {
+ public PublicKey appendPublicKey(Appendable sb, PublicKeyEntryResolver fallbackResolver)
+ throws IOException, GeneralSecurityException {
Map<String, String> options = getLoginOptions();
if (!GenericUtils.isEmpty(options)) {
int index = 0;
@@ -146,7 +146,7 @@ public class AuthorizedKeyEntry extends PublicKeyEntry {
public static List<PublicKey> resolveAuthorizedKeys(
PublicKeyEntryResolver fallbackResolver, Collection<? extends AuthorizedKeyEntry> entries)
- throws IOException, GeneralSecurityException {
+ throws IOException, GeneralSecurityException {
if (GenericUtils.isEmpty(entries)) {
return Collections.emptyList();
}
@@ -163,7 +163,7 @@ public class AuthorizedKeyEntry extends PublicKeyEntry {
}
/**
- * Reads read the contents of an <code>authorized_keys</code> file
+ * Reads read the contents of an {@code authorized_keys} file
*
* @param url The {@link URL} to read from
* @return A {@link List} of all the {@link AuthorizedKeyEntry}-ies found there
@@ -177,7 +177,7 @@ public class AuthorizedKeyEntry extends PublicKeyEntry {
}
/**
- * Reads read the contents of an <code>authorized_keys</code> file
+ * Reads read the contents of an {@code authorized_keys} file
*
* @param path {@link Path} to read from
* @param options The {@link OpenOption}s to use - if unspecified then appropriate
@@ -194,41 +194,26 @@ public class AuthorizedKeyEntry extends PublicKeyEntry {
}
/**
- * Reads read the contents of an <code>authorized_keys</code> file
+ * Reads read the contents of an {@code authorized_keys} 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
+ * @param in The {@link InputStream} to use to read the contents of an {@code authorized_keys} file
+ * @param okToClose {@code true if method may close the input regardless success or failure
* @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)) {
+ 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
+ * Reads read the contents of an {@code authorized_keys} file
*
- * @param rdr The {@link Reader}
- * @param okToClose <code>true</code> if method may close the input stream
- * regardless of whether successful or failed
+ * @param rdr The {@link Reader} to use to read the contents of an {@code authorized_keys} file
+ * @param okToClose {@code true if method may close the input regardless success or failure
* @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)
@@ -240,21 +225,19 @@ public class AuthorizedKeyEntry extends PublicKeyEntry {
}
/**
- * @param rdr The {@link BufferedReader} to use to read the contents of
- * an <code>authorized_keys</code> file
+ * @param rdr The {@link BufferedReader} to use to read the contents of an {@code authorized_keys} 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;
+ if (entry == null) {
+ continue; // null, empty or comment line
}
} catch (RuntimeException | Error e) {
throw new StreamCorruptedException("Failed (" + e.getClass().getSimpleName() + ")"
@@ -276,13 +259,27 @@ public class AuthorizedKeyEntry extends PublicKeyEntry {
}
/**
- * @param value Original line from an <code>authorized_keys</code> file
+ * @param value Original line from an {@code authorized_keys} 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
+ * @see #parseAuthorizedKeyEntry(String, PublicKeyEntryDataResolver)
*/
public static AuthorizedKeyEntry parseAuthorizedKeyEntry(String value) throws IllegalArgumentException {
+ return parseAuthorizedKeyEntry(value, null);
+ }
+
+ /**
+ * @param value Original line from an {@code authorized_keys} file
+ * @param resolver The {@link PublicKeyEntryDataResolver} to use - if {@code null}
+ * one will be automatically resolved from the key type
+ * @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
+ */
+ public static AuthorizedKeyEntry parseAuthorizedKeyEntry(
+ String value, PublicKeyEntryDataResolver resolver)
+ throws IllegalArgumentException {
String line = GenericUtils.replaceWhitespaceAndTrim(value);
if (GenericUtils.isEmpty(line) || (line.charAt(0) == COMMENT_CHAR) /* comment ? */) {
return null;
@@ -310,7 +307,7 @@ public class AuthorizedKeyEntry extends PublicKeyEntry {
} 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 = parsePublicKeyEntry(new AuthorizedKeyEntry(), encData, resolver);
entry.setComment(comment);
}
@@ -318,7 +315,7 @@ public class AuthorizedKeyEntry extends PublicKeyEntry {
}
/**
- * Parses a single line from an <code>authorized_keys</code> file that is <U>known</U>
+ * Parses a single line from an {@code authorized_keys} 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
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index eaec413..0d9c854 100644
--- 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
@@ -22,20 +22,17 @@ package org.apache.sshd.common.config.keys;
import java.security.PrivateKey;
import java.security.PublicKey;
+import org.apache.sshd.common.AlgorithmNameProvider;
import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.OptionalFeature;
/**
- * Represents an SSH key type
+ * Represents an SSH key type - the referenced algorithm is the one used to generate
+ * the key - e.g., "RSA", "DSA", "EC".
*
* @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();
-
+public interface Identity extends AlgorithmNameProvider, NamedResource, OptionalFeature {
Class<? extends PublicKey> getPublicKeyType();
Class<? extends PrivateKey> getPrivateKeyType();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index cf0b1c9..13c854c 100644
--- 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
@@ -31,9 +31,11 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
+import org.apache.sshd.common.AlgorithmNameProvider;
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.keyprovider.KeySizeIndicator;
import org.apache.sshd.common.session.SessionContext;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ValidateUtils;
@@ -52,7 +54,7 @@ import org.apache.sshd.common.util.ValidateUtils;
* @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 class KeyRandomArt implements AlgorithmNameProvider, KeySizeIndicator {
public static final int FLDBASE = 8;
public static final int FLDSIZE_Y = FLDBASE + 1;
public static final int FLDSIZE_X = FLDBASE * 2 + 1;
@@ -117,10 +119,16 @@ public class KeyRandomArt {
field[x][y] = (char) len;
}
+ /**
+ * @return The algorithm that was used to generate the key - e.g.,
+ * "RSA", "DSA", "EC".
+ */
+ @Override
public String getAlgorithm() {
return algorithm;
}
+ @Override
public int getKeySize() {
return keySize;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index c8cfe9c..fec4f4d 100644
--- 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
@@ -55,9 +55,11 @@ import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.NavigableSet;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
+import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.sshd.common.Factory;
@@ -292,8 +294,7 @@ public final class KeyUtils {
/**
* @param decoder The decoder to register
- * @throws IllegalArgumentException if no decoder or not key type or no
- * supported names for the decoder
+ * @throws IllegalArgumentException if no decoder or not key type or no supported names for the decoder
* @see PublicKeyEntryDecoder#getPublicKeyType()
* @see PublicKeyEntryDecoder#getSupportedTypeNames()
*/
@@ -307,21 +308,111 @@ public final class KeyUtils {
BY_KEY_CLASS_DECODERS_MAP.put(prvType, decoder);
}
- Collection<String> names = ValidateUtils.checkNotNullAndNotEmpty(decoder.getSupportedTypeNames(), "No supported key type");
+ registerPublicKeyEntryDecoderKeyTypes(decoder);
+ }
+
+ /**
+ * Registers the specified decoder for all the types it {@link PublicKeyEntryDecoder#getSupportedTypeNames() supports}
+ *
+ * @param decoder The (never {@code null}) {@link PublicKeyEntryDecoder decoder} to register
+ * @see #registerPublicKeyEntryDecoderForKeyType(String, PublicKeyEntryDecoder)
+ */
+ public static void registerPublicKeyEntryDecoderKeyTypes(PublicKeyEntryDecoder<?, ?> decoder) {
+ Objects.requireNonNull(decoder, "No decoder specified");
+
+ Collection<String> names =
+ ValidateUtils.checkNotNullAndNotEmpty(decoder.getSupportedTypeNames(), "No supported key types");
+ for (String n : names) {
+ PublicKeyEntryDecoder<?, ?> prev = registerPublicKeyEntryDecoderForKeyType(n, decoder);
+ if (prev != null) {
+ //noinspection UnnecessaryContinue
+ continue; // debug breakpoint
+ }
+ }
+ }
+
+ /**
+ * @param keyType The key (never {@code null}/empty) key type
+ * @param decoder The (never {@code null}) {@link PublicKeyEntryDecoder decoder} to register
+ * @return The previously registered decoder for this key type - {@code null} if none
+ */
+ public static PublicKeyEntryDecoder<?, ?> registerPublicKeyEntryDecoderForKeyType(String keyType, PublicKeyEntryDecoder<?, ?> decoder) {
+ keyType = ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type specified");
+ Objects.requireNonNull(decoder, "No decoder specified");
+
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
- }
+ return BY_KEY_TYPE_DECODERS_MAP.put(keyType, decoder);
+ }
+ }
+
+ /**
+ * @param decoder The (never {@code null}) {@link PublicKeyEntryDecoder decoder} to unregister
+ * @return The case <U>insensitive</U> {@link NavigableSet} of all the effectively un-registered key types
+ * out of all the {@link PublicKeyEntryDecoder#getSupportedTypeNames() supported} ones.
+ * @see #unregisterPublicKeyEntryDecoderKeyTypes(PublicKeyEntryDecoder)
+ */
+ public static NavigableSet<String> unregisterPublicKeyEntryDecoder(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.remove(pubType);
+ BY_KEY_CLASS_DECODERS_MAP.remove(prvType);
+ }
+
+ return unregisterPublicKeyEntryDecoderKeyTypes(decoder);
+ }
+
+ /**
+ * Unregisters the specified decoder for all the types it supports
+ *
+ * @param decoder The (never {@code null}) {@link PublicKeyEntryDecoder decoder} to unregister
+ * @return The case <U>insensitive</U> {@link NavigableSet} of all the effectively un-registered key types
+ * out of all the {@link PublicKeyEntryDecoder#getSupportedTypeNames() supported} ones.
+ * @see #unregisterPublicKeyEntryDecoderForKeyType(String)
+ */
+ public static NavigableSet<String> unregisterPublicKeyEntryDecoderKeyTypes(PublicKeyEntryDecoder<?, ?> decoder) {
+ Objects.requireNonNull(decoder, "No decoder specified");
+
+ Collection<String> names = ValidateUtils.checkNotNullAndNotEmpty(
+ decoder.getSupportedTypeNames(), "No supported key types");
+ NavigableSet<String> removed = Collections.emptyNavigableSet();
+ for (String n : names) {
+ PublicKeyEntryDecoder<?, ?> prev = unregisterPublicKeyEntryDecoderForKeyType(n);
+ if (prev == null) {
+ continue;
+ }
+
+ if (removed.isEmpty()) {
+ removed = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+ }
+
+ if (!removed.add(n)) {
+ //noinspection UnnecessaryContinue
+ continue; // debug breakpoint
}
}
+
+ return removed;
+ }
+
+ /**
+ * Unregister the decoder registered for the specified key type
+ *
+ * @param keyType The key (never {@code null}/empty) key type
+ * @return The unregistered {@link PublicKeyEntryDecoder} - {@code null} if none registered for this key type
+ */
+ public static PublicKeyEntryDecoder<?, ?> unregisterPublicKeyEntryDecoderForKeyType(String keyType) {
+ keyType = ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type specified");
+
+ synchronized (BY_KEY_TYPE_DECODERS_MAP) {
+ return BY_KEY_TYPE_DECODERS_MAP.remove(keyType);
+ }
}
/**
- * @param keyType The {@code OpenSSH} key type string - e.g., {@code ssh-rsa, ssh-dss}
- * - ignored if {@code null}/empty
+ * @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) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index 1db3d2b..49a5e41 100644
--- 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
@@ -28,11 +28,15 @@ import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
-import java.util.Base64;
+import java.util.Collections;
+import java.util.NavigableMap;
import java.util.Objects;
+import java.util.TreeMap;
+import org.apache.sshd.common.keyprovider.KeyTypeIndicator;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
/**
* <P>Represents a {@link PublicKey} whose data is formatted according to
@@ -44,14 +48,12 @@ import org.apache.sshd.common.util.NumberUtils;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public class PublicKeyEntry implements Serializable {
-
+public class PublicKeyEntry implements Serializable, KeyTypeIndicator {
/**
* 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
*/
@@ -59,8 +61,11 @@ public class PublicKeyEntry implements Serializable {
private static final long serialVersionUID = -585506072687602760L;
+ private static final NavigableMap<String, PublicKeyEntryDataResolver> KEY_DATA_RESOLVERS = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
private String keyType;
private byte[] keyData;
+ private PublicKeyEntryDataResolver keyDataResolver = PublicKeyEntryDataResolver.DEFAULT;
public PublicKeyEntry() {
super();
@@ -71,6 +76,7 @@ public class PublicKeyEntry implements Serializable {
this.keyData = keyData;
}
+ @Override
public String getKeyType() {
return keyType;
}
@@ -87,6 +93,25 @@ public class PublicKeyEntry implements Serializable {
this.keyData = value;
}
+ public PublicKeyEntryDataResolver getKeyDataResolver() {
+ return keyDataResolver;
+ }
+
+ public void setKeyDataResolver(PublicKeyEntryDataResolver keyDataResolver) {
+ this.keyDataResolver = keyDataResolver;
+ }
+
+ /**
+ * If a {@link PublicKeyEntryDataResolver} has been set, then uses it - otherwise
+ * uses the {@link PublicKeyEntryDataResolver#DEFAULT default one}.
+ *
+ * @return The resolved instance
+ */
+ public PublicKeyEntryDataResolver resolvePublicKeyEntryDataResolver() {
+ PublicKeyEntryDataResolver resolver = getKeyDataResolver();
+ return (resolver == null) ? PublicKeyEntryDataResolver.DEFAULT : resolver;
+ }
+
/**
* @param fallbackResolver The {@link PublicKeyEntryResolver} to consult if
* none of the built-in ones can be used. If {@code null} and no built-in
@@ -97,7 +122,8 @@ public class PublicKeyEntry implements Serializable {
* @throws IOException If failed to decode the key
* @throws GeneralSecurityException If failed to generate the key
*/
- public PublicKey resolvePublicKey(PublicKeyEntryResolver fallbackResolver) throws IOException, GeneralSecurityException {
+ public PublicKey resolvePublicKey(PublicKeyEntryResolver fallbackResolver)
+ throws IOException, GeneralSecurityException {
String kt = getKeyType();
PublicKeyEntryResolver decoder = KeyUtils.getPublicKeyEntryDecoder(kt);
if (decoder == null) {
@@ -120,10 +146,11 @@ public class PublicKeyEntry implements Serializable {
* @throws GeneralSecurityException If failed to generate the key
* @see #resolvePublicKey(PublicKeyEntryResolver)
*/
- public PublicKey appendPublicKey(Appendable sb, PublicKeyEntryResolver fallbackResolver) throws IOException, GeneralSecurityException {
+ public PublicKey appendPublicKey(Appendable sb, PublicKeyEntryResolver fallbackResolver)
+ throws IOException, GeneralSecurityException {
PublicKey key = resolvePublicKey(fallbackResolver);
if (key != null) {
- appendPublicKeyEntry(sb, key);
+ appendPublicKeyEntry(sb, key, resolvePublicKeyEntryDataResolver());
}
return key;
}
@@ -161,9 +188,82 @@ public class PublicKeyEntry implements Serializable {
@Override
public String toString() {
- byte[] data = getKeyData();
- Base64.Encoder encoder = Base64.getEncoder();
- return getKeyType() + " " + (NumberUtils.isEmpty(data) ? "<no-key>" : encoder.encodeToString(data));
+ PublicKeyEntryDataResolver resolver = resolvePublicKeyEntryDataResolver();
+ String encData = resolver.encodeEntryKeyData(getKeyData());
+ return getKeyType() + " " + (GenericUtils.isEmpty(encData) ? "<no-key>" : encData);
+ }
+
+ /**
+ * Registers a specialized decoder for the public key entry data bytes instead of the
+ * {@link PublicKeyEntryDataResolver#DEFAULT default} one.
+ *
+ * @param keyType The key-type value (case <U>insensitive</U>) that will trigger the
+ * usage of this decoder - e.g., "ssh-rsa", "pgp-sign-dss", etc.
+ * @param decoder The decoder to use
+ */
+ public static void registerKeyDataEntryResolver(String keyType, PublicKeyEntryDataResolver resolver) {
+ ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type provided");
+ Objects.requireNonNull(resolver, "No resolver provided");
+
+ synchronized (KEY_DATA_RESOLVERS) {
+ KEY_DATA_RESOLVERS.put(keyType, resolver);
+ }
+ }
+
+ /**
+ * @param keyType The key-type value (case <U>insensitive</U>) that may have been
+ * previously {@link #registerKeyDataDecoder(String, PublicKeyEntryDataResolver) registered}
+ * - e.g., "ssh-rsa", "pgp-sign-dss", etc.
+ * @return The registered resolver instance - {@code null} if none was registered
+ */
+ public static PublicKeyEntryDataResolver unregisterKeyDataEntryResolver(String keyType) {
+ keyType = ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type provided");
+
+ synchronized (KEY_DATA_RESOLVERS) {
+ return KEY_DATA_RESOLVERS.remove(keyType);
+ }
+ }
+
+ /**
+ * @param keyType keyType The key-type value (case <U>insensitive</U>) whose data is to
+ * be resolved - e.g., "ssh-rsa", "pgp-sign-dss", etc.
+ * @return If a specific resolver has been previously
+ * {@link #registerKeyDataDecoder(String, PublicKeyEntryDataResolver) registered} then uses it,
+ * otherwise the {@link PublicKeyEntryDataResolver#DEFAULT default} one.
+ */
+ public static PublicKeyEntryDataResolver resolveKeyDataEntryResolver(String keyType) {
+ keyType = ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type provided");
+
+ PublicKeyEntryDataResolver resolver;
+ synchronized (KEY_DATA_RESOLVERS) {
+ resolver = KEY_DATA_RESOLVERS.get(keyType);
+ }
+
+ if (resolver != null) {
+ return resolver; // debug breakpoint
+ }
+
+ return PublicKeyEntryDataResolver.DEFAULT;
+ }
+
+ /**
+ * @return A snapshot of the currently registered specialized {@link PublicKeyEntryDataResolver}-s,
+ * where key=the key-type value (case <U>insensitive</U>) - e.g., "ssh-rsa",
+ * "pgp-sign-dss", etc., value=the associated {@link PublicKeyEntryDataResolver}
+ * for the key type
+ */
+ public static NavigableMap<String, PublicKeyEntryDataResolver> getRegisteredKeyDataEntryResolvers() {
+ NavigableMap<String, PublicKeyEntryDataResolver> decoders;
+ synchronized (KEY_DATA_RESOLVERS) {
+ if (KEY_DATA_RESOLVERS.isEmpty()) {
+ return Collections.emptyNavigableMap();
+ }
+
+ decoders = new TreeMap<>(KEY_DATA_RESOLVERS.comparator());
+ decoders.putAll(KEY_DATA_RESOLVERS);
+ }
+
+ return decoders;
}
/**
@@ -171,27 +271,61 @@ public class PublicKeyEntry implements Serializable {
* (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)
+ * @see #parsePublicKeyEntry(String, PublicKeyEntryDataResolver)
*/
public static PublicKeyEntry parsePublicKeyEntry(String encData) throws IllegalArgumentException {
+ return parsePublicKeyEntry(encData, (PublicKeyEntryDataResolver) 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
+ * @param decoder The {@link PublicKeyEntryDataResolver} to use in order to decode
+ * the key data string into its bytes - if {@code null} then one is
+ * automatically {@link #resolveKeyDataEntryResolver(String) resolved}
+ * @return A {@link PublicKeyEntry} or {@code null} if no data
+ * @throws IllegalArgumentException if bad format found
+ * @see #parsePublicKeyEntry(PublicKeyEntry, String, PublicKeyEntryDataResolver)
+ */
+ public static PublicKeyEntry parsePublicKeyEntry(
+ String encData, PublicKeyEntryDataResolver decoder)
+ throws IllegalArgumentException {
String data = GenericUtils.replaceWhitespaceAndTrim(encData);
if (GenericUtils.isEmpty(data)) {
return null;
} else {
- return parsePublicKeyEntry(new PublicKeyEntry(), data);
+ return parsePublicKeyEntry(new PublicKeyEntry(), data, decoder);
}
}
/**
- * @param <E> The generic entry type
- * @param entry The {@link PublicKeyEntry} whose contents are to be
- * updated - ignored if {@code null}
+ * @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
+ * beyond the BASE64 data is ignored) - ignored if {@code null}/empty
* @return The updated entry instance
* @throws IllegalArgumentException if bad format found
+ * @see #parsePublicKeyEntry(PublicKeyEntry, String, PublicKeyEntryDataResolver)
*/
- public static <E extends PublicKeyEntry> E parsePublicKeyEntry(E entry, String encData) throws IllegalArgumentException {
+ public static <E extends PublicKeyEntry> E parsePublicKeyEntry(E entry, String encData)
+ throws IllegalArgumentException {
+ return parsePublicKeyEntry(entry, encData, null);
+ }
+
+ /**
+ * @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
+ * @param decoder The {@link PublicKeyEntryDataResolver} to use in order to decode
+ * the key data string into its bytes - if {@code null} then one is
+ * automatically {@link #resolveKeyDataEntryResolver(String) resolved}
+ * @return The updated entry instance
+ * @throws IllegalArgumentException if bad format found
+ */
+ public static <E extends PublicKeyEntry> E parsePublicKeyEntry(
+ E entry, String encData, PublicKeyEntryDataResolver decoder)
+ throws IllegalArgumentException {
String data = GenericUtils.replaceWhitespaceAndTrim(encData);
if (GenericUtils.isEmpty(data) || (entry == null)) {
return entry;
@@ -203,19 +337,22 @@ public class PublicKeyEntry implements Serializable {
}
int endPos = data.indexOf(' ', startPos + 1);
- if (endPos <= startPos) { // OK if no continuation beyond the BASE64 encoded data
+ if (endPos <= startPos) { // OK if no continuation beyond the encoded key data
endPos = data.length();
}
String keyType = data.substring(0, startPos);
+ if (decoder == null) {
+ decoder = resolveKeyDataEntryResolver(keyType);
+ }
String b64Data = data.substring(startPos + 1, endPos).trim();
- Base64.Decoder decoder = Base64.getDecoder();
- byte[] keyData = decoder.decode(b64Data);
+ byte[] keyData = decoder.decodeEntryKeyData(b64Data);
if (NumberUtils.isEmpty(keyData)) {
throw new IllegalArgumentException("Bad format (no BASE64 key data): " + data);
}
entry.setKeyType(keyType);
+ entry.setKeyDataResolver(decoder);
entry.setKeyData(keyData);
return entry;
}
@@ -224,11 +361,26 @@ public class PublicKeyEntry implements Serializable {
* @param key The {@link PublicKey}
* @return The {@code OpenSSH} encoded data
* @throws IllegalArgumentException If failed to encode
- * @see #appendPublicKeyEntry(Appendable, PublicKey)
+ * @see #toString(PublicKey, PublicKeyEntryDataResolver)
*/
public static String toString(PublicKey key) throws IllegalArgumentException {
+ return toString(key, null);
+ }
+
+ /**
+ * @param key The {@link PublicKey}
+ * @param encoder The {@link PublicKeyEntryDataResolver} to use in order to encode
+ * the key data bytes into a string representation - if {@code null} then one is
+ * automatically {@link #resolveKeyDataEntryResolver(String) resolved}
+ * @return The {@code OpenSSH} encoded data
+ * @throws IllegalArgumentException If failed to encode
+ * @see #appendPublicKeyEntry(Appendable, PublicKey, PublicKeyEntryDataResolver)
+ */
+ public static String toString(
+ PublicKey key, PublicKeyEntryDataResolver encoder)
+ throws IllegalArgumentException {
try {
- return appendPublicKeyEntry(new StringBuilder(Byte.MAX_VALUE), key).toString();
+ return appendPublicKeyEntry(new StringBuilder(Byte.MAX_VALUE), key, encoder).toString();
} catch (IOException e) {
throw new IllegalArgumentException("Failed (" + e.getClass().getSimpleName() + ") to encode: " + e.getMessage(), e);
}
@@ -242,8 +394,25 @@ public class PublicKeyEntry implements Serializable {
* @param key The {@link PublicKey} - ignored if {@code null}
* @return The updated appendable instance
* @throws IOException If failed to append the data
+ * @see #appendPublicKeyEntry(Appendable, PublicKey, PublicKeyEntryDataResolver)
*/
public static <A extends Appendable> A appendPublicKeyEntry(A sb, PublicKey key) throws IOException {
+ return appendPublicKeyEntry(sb, key, null);
+ }
+
+ /**
+ * @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}
+ * @param encoder The {@link PublicKeyEntryDataResolver} to use in order to encode
+ * the key data bytes into a string representation - if {@code null} then one is
+ * automatically {@link #resolveKeyDataEntryResolver(String) resolved}
+ * @return The updated appendable instance
+ * @throws IOException If failed to append the data
+ */
+ public static <A extends Appendable> A appendPublicKeyEntry(
+ A sb, PublicKey key, PublicKeyEntryDataResolver encoder)
+ throws IOException {
if (key == null) {
return sb;
}
@@ -258,9 +427,12 @@ public class PublicKeyEntry implements Serializable {
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);
+ if (encoder == null) {
+ encoder = resolveKeyDataEntryResolver(keyType);
+ }
+
+ String encData = encoder.encodeEntryKeyData(bytes);
+ sb.append(keyType).append(' ').append(encData);
}
return sb;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDataResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDataResolver.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDataResolver.java
new file mode 100644
index 0000000..96a4bb9
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/PublicKeyEntryDataResolver.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.config.keys;
+
+import java.util.Base64;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface PublicKeyEntryDataResolver {
+ PublicKeyEntryDataResolver DEFAULT = new PublicKeyEntryDataResolver() {
+ @Override
+ public String toString() {
+ return "DEFAULT";
+ }
+ };
+
+ /**
+ * Decodes the public key entry data bytes from their string representation - by
+ * default it assume {@link Base64} encoding.
+ *
+ * @param encData The encoded data - ignored if {@code null}/empty
+ * @return The decoded data bytes
+ */
+ default byte[] decodeEntryKeyData(String encData) {
+ if (GenericUtils.isEmpty(encData)) {
+ return GenericUtils.EMPTY_BYTE_ARRAY;
+ }
+
+ Base64.Decoder decoder = Base64.getDecoder();
+ return decoder.decode(encData);
+ }
+
+ /**
+ * Encodes the public key entry data bytes into their string representation - by
+ * default it assume {@link Base64} encoding.
+ *
+ * @param keyData The key data bytes - ignored if {@code null}/empty
+ * @return The encoded data bytes
+ */
+ default String encodeEntryKeyData(byte[] keyData) {
+ if (NumberUtils.isEmpty(keyData)) {
+ return "";
+ }
+
+ Base64.Encoder encoder = Base64.getEncoder();
+ return encoder.encodeToString(keyData);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index ad42781..1d6b7aa 100644
--- 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
@@ -50,34 +50,37 @@ public interface PublicKeyEntryDecoder<PUB extends PublicKey, PRV extends Privat
ValidateUtils.checkNotNullAndNotEmpty(keyType, "No key type provided");
Collection<String> supported = getSupportedTypeNames();
if ((GenericUtils.size(supported) > 0) && supported.contains(keyType)) {
- return decodePublicKey(keyData);
+ return decodePublicKey(keyType, 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
+ * @param keyType The {@code OpenSSH} reported key type
+ * @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(String keyType, byte... keyData) throws IOException, GeneralSecurityException {
+ return decodePublicKey(keyType, keyData, 0, NumberUtils.length(keyData));
}
- default PUB decodePublicKey(byte[] keyData, int offset, int length) throws IOException, GeneralSecurityException {
+ default PUB decodePublicKey(String keyType, 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);
+ return decodePublicKeyByType(keyType, stream);
}
}
- default PUB decodePublicKey(InputStream keyData) throws IOException, GeneralSecurityException {
+ default PUB decodePublicKeyByType(String keyType, InputStream keyData)
+ throws IOException, GeneralSecurityException {
// the actual data is preceded by a string that repeats the key type
String type = KeyEntryResolver.decodeString(keyData, KeyPairResourceLoader.MAX_KEY_TYPE_NAME_LENGTH);
if (GenericUtils.isEmpty(type)) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index 07c7e44..d9457f7 100644
--- 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
@@ -19,17 +19,15 @@
package org.apache.sshd.common.config.keys.loader.pem;
+import org.apache.sshd.common.AlgorithmNameProvider;
import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
/**
+ * The reported algorithm name refers to the encryption algorithm name - e.g., "RSA", "DSA"
+ *
* @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();
-
+public interface KeyPairPEMResourceParser extends AlgorithmNameProvider, KeyPairResourceParser {
/**
* @return The OID used to identify this algorithm in DER encodings - e.g., RSA=1.2.840.113549.1.1.1
*/
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index ba181dc..400d14a 100644
--- 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
@@ -19,18 +19,16 @@
package org.apache.sshd.common.digest;
+import org.apache.sshd.common.AlgorithmNameProvider;
+
/**
+ * The reported algorithm name refers to the type of digest being calculated.
+ *
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface DigestInformation {
- /**
- * @return The digest algorithm name
- */
- String getAlgorithm();
-
+public interface DigestInformation extends AlgorithmNameProvider {
/**
* @return The number of bytes in the digest's output
*/
int getBlockSize();
-
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeySizeIndicator.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeySizeIndicator.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeySizeIndicator.java
new file mode 100644
index 0000000..1a8e456
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeySizeIndicator.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.keyprovider;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface KeySizeIndicator {
+ /**
+ * @return The number of bits used in the key
+ */
+ int getKeySize();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyTypeIndicator.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyTypeIndicator.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyTypeIndicator.java
new file mode 100644
index 0000000..1830447
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyTypeIndicator.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.keyprovider;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface KeyTypeIndicator {
+ /**
+ * @return The <U>SSH</U> key type name - e.g., "ssh-rsa", "sshd-dss" etc.
+ */
+ String getKeyType();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index 583165f..07a9321 100644
--- 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
@@ -19,15 +19,14 @@
package org.apache.sshd.common.mac;
+import org.apache.sshd.common.AlgorithmNameProvider;
+
/**
+ * The reported algorithm name refers to the MAC being used
+ *
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface MacInformation {
- /**
- * @return MAC algorithm name
- */
- String getAlgorithm();
-
+public interface MacInformation extends AlgorithmNameProvider {
/**
* @return MAC output block size in bytes - may be less than the default
* - e.g., MD5-96
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index fd88a1d..2c78ec1 100644
--- 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
@@ -21,20 +21,17 @@ package org.apache.sshd.common.signature;
import java.security.PrivateKey;
import java.security.PublicKey;
+import org.apache.sshd.common.AlgorithmNameProvider;
import org.apache.sshd.common.util.NumberUtils;
/**
- * Signature interface for SSH used to sign or verify packets
- * Usually wraps a javax.crypto.Signature object
+ * Signature interface for SSH used to sign or verify packets. Usually wraps a
+ * {@code javax.crypto.Signature} object. The reported algorithm name refers to
+ * the signature type being applied.
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface Signature {
- /**
- * @return The signature algorithm name
- */
- String getAlgorithm();
-
+public interface Signature extends AlgorithmNameProvider {
/**
* @param key The {@link PublicKey} to be used for verifying signatures
* @throws Exception If failed to initialize
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index c13cd9e..7fff5d0 100644
--- 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
@@ -65,19 +65,27 @@ public final class BufferUtils {
throw new UnsupportedOperationException("No instance allowed");
}
- public static void dumpHex(SimplifiedLog logger, Level level, String prefix, PropertyResolver resolver, char sep, byte... data) {
+ 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, 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) {
+ 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) {
+ 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;
}
@@ -151,7 +159,9 @@ public final class BufferUtils {
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 {
+ public static <A extends Appendable> A appendHex(
+ A sb, byte[] array, int offset, int len, char sep)
+ throws IOException {
if (len <= 0) {
return sb;
}
@@ -244,7 +254,9 @@ public final class BufferUtils {
* @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 {
+ 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);
@@ -399,7 +411,9 @@ public final class BufferUtils {
* @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 {
+ public static void writeInt(
+ OutputStream output, int value, byte[] buf, int off, int len)
+ throws IOException {
writeUInt(output, value & 0xFFFFFFFFL, buf, off, len);
}
@@ -427,7 +441,9 @@ public final class BufferUtils {
* @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 {
+ 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);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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
index 287abec..1ed3a6c 100644
--- 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
@@ -35,11 +35,13 @@ import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
+import org.apache.sshd.common.AlgorithmNameProvider;
import org.apache.sshd.common.NamedResource;
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.keyprovider.KeySizeIndicator;
import org.apache.sshd.common.session.SessionContext;
import org.apache.sshd.common.util.io.IoUtils;
import org.apache.sshd.common.util.io.resource.PathResource;
@@ -53,7 +55,9 @@ import org.apache.sshd.common.util.security.SecurityUtils;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairProvider {
+public abstract class AbstractGeneratorHostKeyProvider
+ extends AbstractKeyPairProvider
+ implements AlgorithmNameProvider, KeySizeIndicator {
public static final String DEFAULT_ALGORITHM = KeyUtils.RSA_ALGORITHM;
public static final boolean DEFAULT_ALLOWED_TO_OVERWRITE = true;
@@ -77,6 +81,7 @@ public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairPr
this.path = (path == null) ? null : path.toAbsolutePath();
}
+ @Override
public String getAlgorithm() {
return algorithm;
}
@@ -85,6 +90,7 @@ public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairPr
this.algorithm = algorithm;
}
+ @Override
public int getKeySize() {
return keySize;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/KeySetPublickeyAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/KeySetPublickeyAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/KeySetPublickeyAuthenticator.java
index fcba948..3c70f2e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/KeySetPublickeyAuthenticator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/KeySetPublickeyAuthenticator.java
@@ -21,6 +21,7 @@ package org.apache.sshd.server.auth.pubkey;
import java.security.PublicKey;
import java.util.Collection;
import java.util.Collections;
+import java.util.Objects;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.util.GenericUtils;
@@ -32,11 +33,20 @@ import org.apache.sshd.server.session.ServerSession;
*/
public class KeySetPublickeyAuthenticator extends AbstractLoggingBean implements PublickeyAuthenticator {
private final Collection<? extends PublicKey> keySet;
+ private final Object id;
- public KeySetPublickeyAuthenticator(Collection<? extends PublicKey> keySet) {
+ public KeySetPublickeyAuthenticator(Object id, Collection<? extends PublicKey> keySet) {
+ this.id = id;
this.keySet = (keySet == null) ? Collections.emptyList() : keySet;
}
+ /**
+ * @return Some kind of mnemonic identifier for the authenticator - used also in {@link #toString()}
+ */
+ public Object getId() {
+ return id;
+ }
+
public final Collection<? extends PublicKey> getKeySet() {
return keySet;
}
@@ -62,4 +72,9 @@ public class KeySetPublickeyAuthenticator extends AbstractLoggingBean implements
}
return matchFound;
}
+
+ @Override
+ public String toString() {
+ return Objects.toString(getId());
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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 a498478..b6a8ab6 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
@@ -49,14 +49,23 @@ public interface PublickeyAuthenticator {
*/
boolean authenticate(String username, PublicKey key, ServerSession session) throws AsyncAuthException;
+ /**
+ * @param id Some kind of mnemonic identifier for the authenticator - used also in {@link #toString()}
+ * @param fallbackResolver The public key resolver to use if none of the default registered ones works
+ * @param entries The entries to parse - ignored if {@code null}/empty
+ * @return A wrapper with all the parsed keys
+ * @throws IOException If failed to parse the keys data
+ * @throws GeneralSecurityException If failed to generate the relevant keys from the parsed data
+ */
static PublickeyAuthenticator fromAuthorizedEntries(
- PublicKeyEntryResolver fallbackResolver, Collection<? extends AuthorizedKeyEntry> entries)
- throws IOException, GeneralSecurityException {
- Collection<PublicKey> keys = AuthorizedKeyEntry.resolveAuthorizedKeys(fallbackResolver, entries);
+ Object id, 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);
+ return new KeySetPublickeyAuthenticator(id, keys);
}
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2f92fe5d/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 a2d577f..3e9ca3a 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
@@ -82,7 +82,8 @@ public class AuthorizedKeysAuthenticator extends ModifiableFileWatcher implement
boolean debugEnabled = log.isDebugEnabled();
if (!isValidUsername(username, session)) {
if (debugEnabled) {
- log.debug("authenticate(" + username + ")[" + session + "][" + key.getAlgorithm() + "] invalid user name - file = " + getPath());
+ log.debug("authenticate({})[{}][{}] invalid user name - file = {}",
+ username, session, key.getAlgorithm(), getPath());
}
return false;
}
@@ -92,15 +93,15 @@ public class AuthorizedKeysAuthenticator extends ModifiableFileWatcher implement
Objects.requireNonNull(resolvePublickeyAuthenticator(username, session), "No delegate");
boolean accepted = delegate.authenticate(username, key, session);
if (debugEnabled) {
- log.debug("authenticate(" + username + ")[" + session + "][" + key.getAlgorithm() + "] accepted " + accepted + " from " + getPath());
+ log.debug("authenticate({})[{}][{}] invalid user name - accepted={} from file = {}",
+ username, session, key.getAlgorithm(), accepted, getPath());
}
return accepted;
} catch (Throwable e) {
if (debugEnabled) {
- log.debug("authenticate(" + username + ")[" + session + "][" + getPath() + "]"
- + " failed (" + e.getClass().getSimpleName() + ")"
- + " to resolve delegate: " + e.getMessage());
+ log.debug("authenticate({})[{}] failed ({}) to authenticate {} key from {}: {}",
+ username, session, e.getClass().getSimpleName(), key.getAlgorithm(), getPath(), e.getMessage());
}
if (log.isTraceEnabled()) {
@@ -127,24 +128,33 @@ public class AuthorizedKeysAuthenticator extends ModifiableFileWatcher implement
Collection<AuthorizedKeyEntry> entries = reloadAuthorizedKeys(path, username, session);
if (GenericUtils.size(entries) > 0) {
PublickeyAuthenticator authDelegate =
- PublickeyAuthenticator.fromAuthorizedEntries(getFallbackPublicKeyEntryResolver(), entries);
+ createDelegateAuthenticator(path, entries, getFallbackPublicKeyEntryResolver());
delegateHolder.set(authDelegate);
}
} else {
- log.info("resolvePublickeyAuthenticator(" + username + ")[" + session + "] no authorized keys file at " + path);
+ log.info("resolvePublickeyAuthenticator({})[{}] no authorized keys file at {}", username, session, path);
}
}
return delegateHolder.get();
}
+ protected PublickeyAuthenticator createDelegateAuthenticator(
+ Path path, Collection<AuthorizedKeyEntry> entries, PublicKeyEntryResolver fallbackResolver)
+ throws IOException, GeneralSecurityException {
+ return PublickeyAuthenticator.fromAuthorizedEntries(path, fallbackResolver, entries);
+ }
+
protected PublicKeyEntryResolver getFallbackPublicKeyEntryResolver() {
return PublicKeyEntryResolver.IGNORING;
}
- protected Collection<AuthorizedKeyEntry> reloadAuthorizedKeys(Path path, String username, ServerSession session) throws IOException {
+ protected Collection<AuthorizedKeyEntry> reloadAuthorizedKeys(
+ Path path, String username, ServerSession session)
+ throws IOException, GeneralSecurityException {
Collection<AuthorizedKeyEntry> entries = AuthorizedKeyEntry.readAuthorizedKeys(path);
- log.info("reloadAuthorizedKeys(" + username + ")[" + session + "] loaded " + GenericUtils.size(entries) + " keys from " + path);
+ log.info("reloadAuthorizedKeys({})[{}] loaded {} keys from {}",
+ username, session, GenericUtils.size(entries), path);
updateReloadAttributes();
return entries;
}