You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2015/06/21 14:51:37 UTC
[2/2] mina-sshd git commit: [SSHD-495] Move all code that calls
Bouncycastle code to SecurityUtils
[SSHD-495] Move all code that calls Bouncycastle code to SecurityUtils
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/06d37a2d
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/06d37a2d
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/06d37a2d
Branch: refs/heads/master
Commit: 06d37a2d5d3727b28f833f56a01b4d7637e084ae
Parents: 189d8cb
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Sun Jun 21 15:51:25 2015 +0300
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Sun Jun 21 15:51:25 2015 +0300
----------------------------------------------------------------------
.../java/org/apache/sshd/client/SshClient.java | 39 ++-
.../org/apache/sshd/common/BaseBuilder.java | 8 +-
.../config/keys/FilePasswordProvider.java | 35 +++
...actClassLoadableResourceKeyPairProvider.java | 89 +++++++
.../AbstractFileKeyPairProvider.java | 94 +++++++
.../AbstractResourceKeyPairProvider.java | 199 ++++++++++++++
.../common/keyprovider/FileKeyPairProvider.java | 156 -----------
.../keyprovider/ResourceKeyPairProvider.java | 206 --------------
.../sshd/common/random/BouncyCastleRandom.java | 103 -------
.../apache/sshd/common/random/JceRandom.java | 23 --
.../sshd/common/random/JceRandomFactory.java | 46 ++++
.../sshd/common/random/RandomFactory.java | 37 +++
.../common/random/SingletonRandomFactory.java | 12 +-
.../apache/sshd/common/util/SecurityUtils.java | 266 ++++++++++++++++++-
.../apache/sshd/common/util/ValidateUtils.java | 6 +
.../org/apache/sshd/common/util/io/IoUtils.java | 7 +-
.../common/util/io/ModifiableFileWatcher.java | 2 +-
.../java/org/apache/sshd/server/SshServer.java | 7 +-
.../AbstractGeneratorHostKeyProvider.java | 136 +++++-----
.../PEMGeneratorHostKeyProvider.java | 79 ------
.../SimpleGeneratorHostKeyProvider.java | 32 ++-
.../apache/sshd/server/sftp/SftpSubsystem.java | 2 +-
.../apache/sshd/SinglePublicKeyAuthTest.java | 8 +-
.../java/org/apache/sshd/client/ClientTest.java | 5 +-
.../client/session/ClientSessionImplTest.java | 4 +-
.../apache/sshd/common/cipher/CipherTest.java | 5 +-
.../org/apache/sshd/common/mac/MacTest.java | 5 +-
.../apache/sshd/common/random/RandomTest.java | 16 +-
.../sshd/common/util/SecurityUtilsTest.java | 173 ++++++++++++
.../AbstractGeneratorHostKeyProviderTest.java | 9 +-
.../PEMGeneratorHostKeyProviderTest.java | 6 +-
.../SimpleGeneratorHostKeyProviderTest.java | 2 +-
.../org/apache/sshd/util/BaseTestSupport.java | 35 ++-
.../test/java/org/apache/sshd/util/Utils.java | 40 ++-
.../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 +
.../java/org/apache/sshd/git/util/Utils.java | 8 +-
53 files changed, 1344 insertions(+), 742 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/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 f223119..8bea7ed 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
@@ -32,7 +32,6 @@ import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.Callable;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
@@ -58,10 +57,11 @@ import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.SshdSocketAddress;
import org.apache.sshd.common.channel.Channel;
import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
import org.apache.sshd.common.future.SshFutureListener;
import org.apache.sshd.common.io.IoConnectFuture;
import org.apache.sshd.common.io.IoConnector;
-import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+import org.apache.sshd.common.keyprovider.AbstractFileKeyPairProvider;
import org.apache.sshd.common.keyprovider.KeyPairProvider;
import org.apache.sshd.common.session.AbstractSession;
import org.apache.sshd.common.util.GenericUtils;
@@ -69,7 +69,6 @@ import org.apache.sshd.common.util.SecurityUtils;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.io.NoCloseInputStream;
import org.apache.sshd.common.util.io.NoCloseOutputStream;
-import org.bouncycastle.openssl.PasswordFinder;
/**
* Entry point for the client side of the SSH protocol.
@@ -437,43 +436,35 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
}
KeyPairProvider provider = null;
- final List<String> files = new ArrayList<String>();
+ final List<File> files = new ArrayList<File>();
File f = new File(System.getProperty("user.home"), ".ssh/id_dsa");
if (f.exists() && f.isFile() && f.canRead()) {
- files.add(f.getAbsolutePath());
+ files.add(f);
}
f = new File(System.getProperty("user.home"), ".ssh/id_rsa");
if (f.exists() && f.isFile() && f.canRead()) {
- files.add(f.getAbsolutePath());
+ files.add(f);
}
f = new File(System.getProperty("user.home"), ".ssh/id_ecdsa");
if (f.exists() && f.isFile() && f.canRead()) {
- files.add(f.getAbsolutePath());
+ files.add(f);
}
if (files.size() > 0) {
// SSHD-292: we need to use a different class to load the FileKeyPairProvider
// in order to break the link between SshClient and BouncyCastle
try {
if (SecurityUtils.isBouncyCastleRegistered()) {
- class KeyPairProviderLoader implements Callable<KeyPairProvider> {
+ AbstractFileKeyPairProvider filesProvider=SecurityUtils.createFileKeyPairProvider();
+ filesProvider.setFiles(files);
+ filesProvider.setPasswordFinder(new FilePasswordProvider() {
+ private final BufferedReader r = new BufferedReader(new InputStreamReader(System.in));
@Override
- public KeyPairProvider call() throws Exception {
- return new FileKeyPairProvider(files.toArray(new String[files.size()]), new PasswordFinder() {
- @Override
- public char[] getPassword() {
- try {
- System.out.println("Enter password for private key: ");
- BufferedReader r = new BufferedReader(new InputStreamReader(System.in));
- String password = r.readLine();
- return password.toCharArray();
- } catch (IOException e) {
- return null;
- }
- }
- });
+ public String getPassword(String file) throws IOException {
+ System.out.print("Enter password for private key file=" + file + ": ");
+ return r.readLine();
}
- }
- provider = new KeyPairProviderLoader().call();
+ });
+ provider = filesProvider;
}
} catch (Throwable t) {
System.out.println("Error loading user keys: " + t.getMessage());
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java b/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java
index ee0b2b1..09e62b3 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java
@@ -37,8 +37,6 @@ import org.apache.sshd.common.kex.BuiltinDHFactories;
import org.apache.sshd.common.kex.KeyExchange;
import org.apache.sshd.common.mac.BuiltinMacs;
import org.apache.sshd.common.mac.Mac;
-import org.apache.sshd.common.random.BouncyCastleRandom;
-import org.apache.sshd.common.random.JceRandom;
import org.apache.sshd.common.random.Random;
import org.apache.sshd.common.random.SingletonRandomFactory;
import org.apache.sshd.common.session.ConnectionService;
@@ -76,11 +74,7 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder
}
if (randomFactory == null) {
- if (SecurityUtils.isBouncyCastleRegistered()) {
- randomFactory = new SingletonRandomFactory(BouncyCastleRandom.BouncyCastleRandomFactory.INSTANCE);
- } else {
- randomFactory = new SingletonRandomFactory(JceRandom.JceRandomFactory.INSTANCE);
- }
+ randomFactory = new SingletonRandomFactory(SecurityUtils.getRandomFactory());
}
if (cipherFactories == null) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/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
new file mode 100644
index 0000000..3a6f920
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/keys/FilePasswordProvider.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.config.keys;
+
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface FilePasswordProvider {
+ /**
+ * @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;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractClassLoadableResourceKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractClassLoadableResourceKeyPairProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractClassLoadableResourceKeyPairProvider.java
new file mode 100644
index 0000000..183bf06
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractClassLoadableResourceKeyPairProvider.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.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.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 abstract class AbstractClassLoadableResourceKeyPairProvider extends AbstractResourceKeyPairProvider<String> {
+ private ClassLoader classLoader;
+ private Collection<String> resources;
+
+ protected AbstractClassLoadableResourceKeyPairProvider() {
+ classLoader = ThreadUtils.resolveDefaultClassLoader(getClass());
+ }
+
+ public Collection<String> getResources() {
+ return resources;
+ }
+
+ public void setResources(Collection<String> resources) {
+ this.resources = (resources == null) ? Collections.<String>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/06d37a2d/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractFileKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractFileKeyPairProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractFileKeyPairProvider.java
new file mode 100644
index 0000000..13b4bf3
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractFileKeyPairProvider.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.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.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+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 abstract class AbstractFileKeyPairProvider extends AbstractResourceKeyPairProvider<Path> {
+ private Collection<? extends Path> files;
+
+ protected AbstractFileKeyPairProvider() {
+ super();
+ }
+
+ public Collection<? extends Path> getPaths() {
+ return files;
+ }
+
+ public void setFiles(Collection<File> files) {
+ if (GenericUtils.isEmpty(files)) {
+ setPaths(Collections.<Path>emptyList());
+ } else {
+ List<Path> paths = new ArrayList<Path>(files.size());
+ for (File f : files) {
+ paths.add(f.toPath());
+ }
+ setPaths(paths);
+ }
+ }
+
+ public void setPaths(Collection<? extends Path> paths) {
+ int numPaths = GenericUtils.size(paths);
+ Collection<Path> resolved = (numPaths <= 0) ? Collections.<Path>emptyList() : new ArrayList<Path>(paths.size());
+ // use absolute path in order to have unique cache keys
+ if (numPaths > 0) {
+ for (Path p : paths) {
+ resolved.add(p.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/06d37a2d/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
new file mode 100644
index 0000000..79c4ab9
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/AbstractResourceKeyPairProvider.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * 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.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.util.GenericUtils;
+
+/**
+ * @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, KeyPair>(String.CASE_INSENSITIVE_ORDER);
+
+ protected AbstractResourceKeyPairProvider() {
+ super();
+ }
+
+ public FilePasswordProvider getPasswordFinder() {
+ return passwordFinder;
+ }
+
+ public void setPasswordFinder(FilePasswordProvider passwordFinder) {
+ this.passwordFinder = passwordFinder;
+ }
+
+ protected void resetCacheMap(Collection<?> resources) {
+ // if have any cached pairs then see what we can keep from previous load
+ Collection<String> toDelete=Collections.<String>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 = Objects.toString(r);
+ if (cacheMap.containsKey(resourceKey)) {
+ continue;
+ }
+
+ if (toDelete.isEmpty()) {
+ toDelete = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
+ }
+
+ if (!toDelete.add(resourceKey)) {
+ continue; // debug breakpoint
+ }
+ }
+
+ if (GenericUtils.size(toDelete) > 0) {
+ for (String f : toDelete) {
+ cacheMap.remove(f);
+ }
+ }
+ }
+
+ 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 Iterable<KeyPair>() {
+ @Override
+ public Iterator<KeyPair> iterator() {
+ return new Iterator<KeyPair>() {
+ private final Iterator<? extends R> iterator = resources.iterator();
+ private KeyPair nextKeyPair;
+ private boolean nextKeyPairSet = false;
+
+ @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() {
+ while (iterator.hasNext()) {
+ R r = iterator.next();
+ try {
+ nextKeyPair = doLoadKey(r);
+ } catch(Exception e) {
+ log.warn("Failed (" + e.getClass().getSimpleName() + ")"
+ + " to load key resource=" + r + ": " + e.getMessage());
+ nextKeyPair = null;
+ continue;
+ }
+
+ if (nextKeyPair != null) {
+ nextKeyPairSet = true;
+ return true;
+ }
+ }
+
+ return false;
+ }
+ };
+ }
+ };
+ }
+ }
+
+ protected KeyPair doLoadKey(R resource) throws IOException, GeneralSecurityException {
+ String resourceKey = Objects.toString(resource);
+ KeyPair kp;
+ synchronized(cacheMap) {
+ // check if lucky enough to have already loaded this file
+ if ((kp=cacheMap.get(resourceKey)) != null) {
+ return kp;
+ }
+ }
+
+ if ((kp=doLoadKey(resourceKey, resource, getPasswordFinder())) != null) {
+ synchronized(cacheMap) {
+ // if somebody else beat us to it, use the cached key
+ if (cacheMap.containsKey(resourceKey)) {
+ kp = cacheMap.get(resourceKey);
+ } else {
+ cacheMap.put(resourceKey, kp);
+ }
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("doLoadKey(" + resourceKey + ") loaded " + kp.getPublic() + " / " + kp.getPrivate());
+ }
+ }
+
+ 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 abstract KeyPair doLoadKey(String resourceKey, InputStream inputStream, FilePasswordProvider provider) throws IOException, GeneralSecurityException;
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/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 01d6df9..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.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.keyprovider;
-
-import java.io.FileInputStream;
-import java.io.InputStreamReader;
-import java.security.KeyPair;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-import org.apache.sshd.common.util.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.PasswordFinder;
-import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
-import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
-
-/**
- * This host key provider loads private keys from the specified files.
- *
- * Note that this class has a direct dependency on BouncyCastle and won't work
- * unless it has been correctly registered as a security provider.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class FileKeyPairProvider extends AbstractKeyPairProvider {
-
- private String[] files;
- private PasswordFinder passwordFinder;
-
- public FileKeyPairProvider() {
- super();
- }
-
- public FileKeyPairProvider(String ... files) {
- this(files, null);
- }
-
- public FileKeyPairProvider(String[] files, PasswordFinder passwordFinder) {
- this.files = files;
- this.passwordFinder = passwordFinder;
- }
-
- public String[] getFiles() {
- return files;
- }
-
- public void setFiles(String[] files) {
- this.files = files;
- }
-
- public PasswordFinder getPasswordFinder() {
- return passwordFinder;
- }
-
- public void setPasswordFinder(PasswordFinder passwordFinder) {
- this.passwordFinder = passwordFinder;
- }
-
- @Override
- public Iterable<KeyPair> loadKeys() {
- if (!SecurityUtils.isBouncyCastleRegistered()) {
- throw new IllegalStateException("BouncyCastle must be registered as a JCE provider");
- }
- return new Iterable<KeyPair>() {
- @Override
- public Iterator<KeyPair> iterator() {
- return new Iterator<KeyPair>() {
- @SuppressWarnings("synthetic-access")
- private final Iterator<String> iterator = Arrays.asList(files).iterator();
- private KeyPair nextKeyPair;
- private boolean nextKeyPairSet = false;
- @Override
- public boolean hasNext() {
- return nextKeyPairSet || setNextObject();
- }
- @Override
- public KeyPair next() {
- if (!nextKeyPairSet) {
- if (!setNextObject()) {
- throw new NoSuchElementException();
- }
- }
- nextKeyPairSet = false;
- return nextKeyPair;
- }
- @Override
- public void remove() {
- throw new UnsupportedOperationException("loadKeys(files) Iterator#remove() N/A");
- }
- private boolean setNextObject() {
- while (iterator.hasNext()) {
- String file = iterator.next();
- nextKeyPair = doLoadKey(file);
- if (nextKeyPair != null) {
- nextKeyPairSet = true;
- return true;
- }
- }
- return false;
- }
-
- };
- }
- };
- }
-
- protected KeyPair doLoadKey(String file) {
- try {
- PEMParser r = new PEMParser(new InputStreamReader(new FileInputStream(file)));
- try {
- Object o = r.readObject();
-
- JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
- pemConverter.setProvider("BC");
- if (passwordFinder != null && o instanceof PEMEncryptedKeyPair) {
- JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder();
- PEMDecryptorProvider pemDecryptor = decryptorBuilder.build(passwordFinder.getPassword());
- o = pemConverter.getKeyPair(((PEMEncryptedKeyPair) o).decryptKeyPair(pemDecryptor));
- }
-
- if (o instanceof PEMKeyPair) {
- o = pemConverter.getKeyPair((PEMKeyPair)o);
- return (KeyPair) o;
- } else if (o instanceof KeyPair) {
- return (KeyPair) o;
- }
- } finally {
- r.close();
- }
- } catch (Exception e) {
- log.warn("Unable to read key " + file, e);
- }
- return null;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/ResourceKeyPairProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/ResourceKeyPairProvider.java b/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/ResourceKeyPairProvider.java
deleted file mode 100644
index 134416e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/ResourceKeyPairProvider.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * 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.InputStream;
-import java.io.InputStreamReader;
-import java.security.KeyPair;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.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.PasswordFinder;
-import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
-import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
-
-/**
- * <p>This host key provider loads private keys from the specified resources.</p>
- *
- * <p>Note that this class has a direct dependency on BouncyCastle and won't work
- * unless it has been correctly registered as a security provider.</p>
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class ResourceKeyPairProvider extends AbstractKeyPairProvider {
- // --- Properties ---
-
- /**
- * Class loader
- */
- protected final ClassLoader cloader;
-
- /**
- * Key resources
- */
- private String[] resources;
-
- /**
- * Password finder
- */
- private PasswordFinder passwordFinder;
-
- // ---
-
- /**
- * No-arg constructor.
- */
- public ResourceKeyPairProvider() {
- this(GenericUtils.EMPTY_STRING_ARRAY);
- } // end of <init>
-
- /**
- * Bulk constructor 1.
- */
- public ResourceKeyPairProvider(String ... resources) {
- this(resources, null);
- } // end of <init>
-
- /**
- * Bulk constructor 2.
- */
- public ResourceKeyPairProvider(String[] resources, PasswordFinder passwordFinder) {
- this(resources, passwordFinder, null);
- } // end of <init>
-
- /**
- * Bulk constructor 3.
- */
- public ResourceKeyPairProvider(String[] resources, PasswordFinder passwordFinder, ClassLoader cloader) {
-
- this.cloader = (cloader == null) ? this.getClass().getClassLoader() : cloader;
- this.resources = resources;
- this.passwordFinder = passwordFinder;
- } // end of <init>
-
- // --- Properties accessors ---
-
- /**
- * {@inheritDoc}
- */
- public String[] getResources() {
- return this.resources;
- } // end of getResources
-
- /**
- * {@inheritDoc}
- */
- public void setResources(String[] resources) {
- this.resources = resources;
- } // end of setResources
-
- /**
- * {@inheritDoc}
- */
- public PasswordFinder getPasswordFinder() {
- return this.passwordFinder;
- } // end of getPasswordFinder
-
- /**
- * {@inheritDoc}
- */
- public void setPasswordFinder(PasswordFinder passwordFinder) {
- this.passwordFinder = passwordFinder;
- } // end of setPasswordFinder
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Iterable<KeyPair> loadKeys() {
- if (!SecurityUtils.isBouncyCastleRegistered()) {
- throw new IllegalStateException("BouncyCastle must be registered as a JCE provider");
- } // end of if
- return new Iterable<KeyPair>() {
- @Override
- public Iterator<KeyPair> iterator() {
- return new Iterator<KeyPair>() {
- @SuppressWarnings("synthetic-access")
- private final Iterator<String> iterator = Arrays.asList(resources).iterator();
- private KeyPair nextKeyPair;
- private boolean nextKeyPairSet = false;
- @Override
- public boolean hasNext() {
- return nextKeyPairSet || setNextObject();
- }
- @Override
- public KeyPair next() {
- if (!nextKeyPairSet) {
- if (!setNextObject()) {
- throw new NoSuchElementException("No next element in iterator");
- }
- }
- nextKeyPairSet = false;
- return nextKeyPair;
- }
- @Override
- public void remove() {
- throw new UnsupportedOperationException("Not allowed to remove items");
- }
- private boolean setNextObject() {
- while (iterator.hasNext()) {
- String file = iterator.next();
- nextKeyPair = doLoadKey(file);
- if (nextKeyPair != null) {
- nextKeyPairSet = true;
- return true;
- }
- }
- return false;
- }
-
- };
- }
- };
- }
-
- protected KeyPair doLoadKey(String resource) {
- try(InputStream is = this.cloader.getResourceAsStream(resource);
- InputStreamReader isr = new InputStreamReader(is);
- PEMParser r = new PEMParser(isr)) {
-
- Object o = r.readObject();
-
- JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
- pemConverter.setProvider("BC");
- if ((passwordFinder != null) && (o instanceof PEMEncryptedKeyPair)) {
- JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder();
- PEMDecryptorProvider pemDecryptor = decryptorBuilder.build(passwordFinder.getPassword());
- o = pemConverter.getKeyPair(((PEMEncryptedKeyPair) o).decryptKeyPair(pemDecryptor));
- }
-
- if (o instanceof PEMKeyPair) {
- o = pemConverter.getKeyPair((PEMKeyPair)o);
- return (KeyPair) o;
- } else if (o instanceof KeyPair) {
- return (KeyPair) o;
- } // end of if
- } catch (Exception e) {
- log.warn("Unable to read key " + resource, e);
- }
-
- return null;
- } // end of doLoadKey
-
-} // end of class ResourceKeyPairProvider
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/sshd-core/src/main/java/org/apache/sshd/common/random/BouncyCastleRandom.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/random/BouncyCastleRandom.java b/sshd-core/src/main/java/org/apache/sshd/common/random/BouncyCastleRandom.java
deleted file mode 100644
index acf69d5..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/random/BouncyCastleRandom.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.random;
-
-import java.security.SecureRandom;
-
-import org.apache.sshd.common.NamedFactory;
-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 class BouncyCastleRandom implements Random {
-
- /**
- * Named factory for the BouncyCastle <code>Random</code>
- */
- public static class BouncyCastleRandomFactory implements NamedFactory<Random> {
- public static final BouncyCastleRandomFactory INSTANCE = new BouncyCastleRandomFactory();
-
- public BouncyCastleRandomFactory() {
- super();
- }
-
- @Override
- public String getName() {
- return "bouncycastle";
- }
-
- @Override
- public Random create() {
- return new BouncyCastleRandom();
- }
-
- }
-
- private final RandomGenerator random;
-
- public BouncyCastleRandom() {
- this.random = new VMPCRandomGenerator();
- byte[] seed = new SecureRandom().generateSeed(8);
- this.random.addSeedMaterial(seed);
- }
-
- @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) {
- if (n > 0) {
- if ((n & -n) == n) {
- return (int)((n * (long) next(31)) >> 31);
- }
- int bits, val;
- do {
- bits = next(31);
- val = bits % n;
- } while (bits - val + (n-1) < 0);
- return val;
- }
- throw new IllegalArgumentException("Limit must be positive: " + n);
- }
-
- final protected 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);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/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
index 4657abe..6aaec25 100644
--- 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
@@ -20,8 +20,6 @@ package org.apache.sshd.common.random;
import java.security.SecureRandom;
-import org.apache.sshd.common.NamedFactory;
-
/**
* A <code>Random</code> implementation using the built-in {@link SecureRandom} PRNG.
*
@@ -29,27 +27,6 @@ import org.apache.sshd.common.NamedFactory;
*/
public class JceRandom implements Random {
- /**
- * Named factory for the BouncyCastle <code>Random</code>
- */
- public static class JceRandomFactory implements NamedFactory<Random> {
- public static final JceRandomFactory INSTANCE = new JceRandomFactory();
-
- public JceRandomFactory () {
- super();
- }
-
- @Override
- public String getName() {
- return "default";
- }
-
- @Override
- public Random create() {
- return new JceRandom();
- }
- }
-
private byte[] tmp = new byte[16];
private final SecureRandom random = new SecureRandom();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/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
new file mode 100644
index 0000000..1350814
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/random/JceRandomFactory.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.random;
+
+/**
+ * Named factory for the BouncyCastle <code>Random</code>
+ */
+public class JceRandomFactory implements RandomFactory {
+ public static final JceRandomFactory INSTANCE = new JceRandomFactory();
+
+ public JceRandomFactory () {
+ super();
+ }
+
+ @Override
+ public String getName() {
+ return "default";
+ }
+
+ @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/06d37a2d/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
new file mode 100644
index 0000000..78d348c
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/random/RandomFactory.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.random;
+
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
+import org.apache.sshd.common.util.Transformer;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface RandomFactory extends NamedFactory<Random>, OptionalFeature {
+ // required because of generics issues
+ Transformer<RandomFactory,NamedFactory<Random>> FAC2NAMED=new Transformer<RandomFactory,NamedFactory<Random>>() {
+ @Override
+ public NamedFactory<Random> transform(RandomFactory input) {
+ return input;
+ }
+ };
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/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
index 54b9575..8fc7d48 100644
--- 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
@@ -19,6 +19,7 @@
package org.apache.sshd.common.random;
import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.OptionalFeature;
/**
* A random factory wrapper that uses a single random instance.
@@ -26,7 +27,7 @@ import org.apache.sshd.common.NamedFactory;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public class SingletonRandomFactory implements Random, NamedFactory<Random> {
+public class SingletonRandomFactory implements Random, RandomFactory {
private final NamedFactory<Random> factory;
private final Random random;
@@ -37,6 +38,15 @@ public class SingletonRandomFactory implements Random, NamedFactory<Random> {
}
@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);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
index f23029d..134c9d6 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
@@ -18,10 +18,19 @@
*/
package org.apache.sshd.common.util;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+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.KeyFactory;
+import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
+import java.security.SecureRandom;
import java.security.Signature;
import java.util.concurrent.Callable;
@@ -29,7 +38,22 @@ import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.Mac;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.keyprovider.AbstractClassLoadableResourceKeyPairProvider;
+import org.apache.sshd.common.keyprovider.AbstractFileKeyPairProvider;
+import org.apache.sshd.common.random.JceRandomFactory;
+import org.apache.sshd.common.random.Random;
+import org.apache.sshd.common.random.RandomFactory;
+import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider;
+import org.bouncycastle.crypto.prng.RandomGenerator;
+import org.bouncycastle.crypto.prng.VMPCRandomGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
+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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -110,6 +134,8 @@ public class SecurityUtils {
}
}
+ ///////////////// Bouncycastle specific implementations //////////////////
+
private static class BouncyCastleRegistration implements Callable<Void> {
@SuppressWarnings("synthetic-access")
@Override
@@ -128,66 +154,278 @@ public class SecurityUtils {
}
}
+ /* -------------------------------------------------------------------- */
+
+ // TODO in JDK-8 make this an interface...
+ private static class BouncyCastleInputStreamLoader {
+ 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();
+
+ JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
+ pemConverter.setProvider(SecurityUtils.BOUNCY_CASTLE);
+ 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);
+ }
+ }
+ }
+ }
+
+ /* -------------------------------------------------------------------- */
+
+ // use a separate class in order to avoid direct dependency
+ private static class BouncyCastleFileKeyPairProvider extends AbstractFileKeyPairProvider {
+ private BouncyCastleFileKeyPairProvider() {
+ ValidateUtils.checkTrue(isBouncyCastleRegistered(), "BouncyCastle not registered", GenericUtils.EMPTY_OBJECT_ARRAY);
+ }
+
+ @Override
+ protected KeyPair doLoadKey(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
+ throws IOException, GeneralSecurityException {
+ return BouncyCastleInputStreamLoader.loadKeyPair(resourceKey, inputStream, provider);
+ }
+ }
+
+ @SuppressWarnings("synthetic-access")
+ public static AbstractFileKeyPairProvider createFileKeyPairProvider() {
+ return new BouncyCastleFileKeyPairProvider();
+ }
+
+ /* -------------------------------------------------------------------- */
+
+ private static class BouncyCastleClassLoadableResourceKeyPairProvider extends AbstractClassLoadableResourceKeyPairProvider {
+ private BouncyCastleClassLoadableResourceKeyPairProvider() {
+ ValidateUtils.checkTrue(isBouncyCastleRegistered(), "BouncyCastle not registered", GenericUtils.EMPTY_OBJECT_ARRAY);
+ }
+
+ @Override
+ protected KeyPair doLoadKey(String resourceKey, InputStream inputStream, FilePasswordProvider provider)
+ throws IOException, GeneralSecurityException {
+ return BouncyCastleInputStreamLoader.loadKeyPair(resourceKey, inputStream, provider);
+ }
+ }
+
+ @SuppressWarnings("synthetic-access")
+ public static AbstractClassLoadableResourceKeyPairProvider createClassLoadableResourceKeyPairProvider() {
+ return new BouncyCastleClassLoadableResourceKeyPairProvider();
+ }
+
+ /* -------------------------------------------------------------------- */
+
+ private static class BouncyCastleGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProvider {
+ private BouncyCastleGeneratorHostKeyProvider(Path path) {
+ ValidateUtils.checkTrue(isBouncyCastleRegistered(), "BouncyCastle not registered", GenericUtils.EMPTY_OBJECT_ARRAY);
+ setPath(path);
+ }
+
+ @Override
+ protected KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException {
+ return BouncyCastleInputStreamLoader.loadKeyPair(resourceKey, inputStream, null);
+ }
+
+ @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();
+ }
+ }
+ }
+
+ @SuppressWarnings("synthetic-access")
+ public static AbstractGeneratorHostKeyProvider createGeneratorHostKeyProvider(Path path) {
+ return new BouncyCastleGeneratorHostKeyProvider(path );
+ }
+
+ /* -------------------------------------------------------------------- */
+
+ /**
+ * Named factory for the BouncyCastle <code>Random</code>
+ */
+ private static class BouncyCastleRandomFactory implements RandomFactory {
+ private static final BouncyCastleRandomFactory INSTANCE = new BouncyCastleRandomFactory();
+
+ private BouncyCastleRandomFactory() {
+ super();
+ }
+
+ @Override
+ public boolean isSupported() {
+ return isBouncyCastleRegistered();
+ }
+
+ @Override
+ public String getName() {
+ return "bouncycastle";
+ }
+
+ @Override
+ public Random create() {
+ return new BouncyCastleRandom();
+ }
+ }
+
+ /**
+ * 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>
+ */
+ private static class BouncyCastleRandom implements Random {
+
+ private final RandomGenerator random;
+
+ public BouncyCastleRandom() {
+ ValidateUtils.checkTrue(isBouncyCastleRegistered(), "BouncyCastle not registered", GenericUtils.EMPTY_OBJECT_ARRAY);
+ this.random = new VMPCRandomGenerator();
+ byte[] seed = new SecureRandom().generateSeed(8);
+ this.random.addSeedMaterial(seed);
+ }
+
+ @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) {
+ if (n > 0) {
+ if ((n & -n) == n) {
+ return (int)((n * (long) next(31)) >> 31);
+ }
+ int bits, val;
+ do {
+ bits = next(31);
+ val = bits % n;
+ } while (bits - val + (n-1) < 0);
+ return val;
+ }
+ throw new IllegalArgumentException("Limit must be positive: " + n);
+ }
+
+ final protected 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);
+ }
+ }
+
+ /**
+ * @return If {@link #isBouncyCastleRegistered()} then a {@link BouncyCastleRandomFactory}
+ * instance, otherwise a {@link JceRandomFactory} one
+ */
+ @SuppressWarnings("synthetic-access")
+ public static RandomFactory getRandomFactory() {
+ if (isBouncyCastleRegistered()) {
+ return BouncyCastleRandomFactory.INSTANCE;
+ } else {
+ return JceRandomFactory.INSTANCE;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+
public static synchronized KeyFactory getKeyFactory(String algorithm) throws GeneralSecurityException {
register();
- if (getSecurityProvider() == null) {
+
+ String providerName = getSecurityProvider();
+ if (GenericUtils.isEmpty(providerName)) {
return KeyFactory.getInstance(algorithm);
} else {
- return KeyFactory.getInstance(algorithm, getSecurityProvider());
+ return KeyFactory.getInstance(algorithm, providerName);
}
}
public static synchronized Cipher getCipher(String transformation) throws GeneralSecurityException {
register();
- if (getSecurityProvider() == null) {
+
+ String providerName = getSecurityProvider();
+ if (GenericUtils.isEmpty(providerName)) {
return Cipher.getInstance(transformation);
} else {
- return Cipher.getInstance(transformation, getSecurityProvider());
+ return Cipher.getInstance(transformation, providerName);
}
}
public static synchronized MessageDigest getMessageDigest(String algorithm) throws GeneralSecurityException {
register();
- if (getSecurityProvider() == null) {
+
+ String providerName = getSecurityProvider();
+ if (GenericUtils.isEmpty(providerName)) {
return MessageDigest.getInstance(algorithm);
} else {
- return MessageDigest.getInstance(algorithm, getSecurityProvider());
+ return MessageDigest.getInstance(algorithm, providerName);
}
}
public static synchronized KeyPairGenerator getKeyPairGenerator(String algorithm) throws GeneralSecurityException {
register();
- if (getSecurityProvider() == null) {
+
+ String providerName = getSecurityProvider();
+ if (GenericUtils.isEmpty(providerName)) {
return KeyPairGenerator.getInstance(algorithm);
} else {
- return KeyPairGenerator.getInstance(algorithm, getSecurityProvider());
+ return KeyPairGenerator.getInstance(algorithm, providerName);
}
}
public static synchronized KeyAgreement getKeyAgreement(String algorithm) throws GeneralSecurityException {
register();
- if (getSecurityProvider() == null) {
+
+ String providerName = getSecurityProvider();
+ if (GenericUtils.isEmpty(providerName)) {
return KeyAgreement.getInstance(algorithm);
} else {
- return KeyAgreement.getInstance(algorithm, getSecurityProvider());
+ return KeyAgreement.getInstance(algorithm, providerName);
}
}
public static synchronized Mac getMac(String algorithm) throws GeneralSecurityException {
register();
- if (getSecurityProvider() == null) {
+
+ String providerName = getSecurityProvider();
+ if (GenericUtils.isEmpty(providerName)) {
return Mac.getInstance(algorithm);
} else {
- return Mac.getInstance(algorithm, getSecurityProvider());
+ return Mac.getInstance(algorithm, providerName);
}
}
public static synchronized Signature getSignature(String algorithm) throws GeneralSecurityException {
register();
- if (getSecurityProvider() == null) {
+
+ String providerName = getSecurityProvider();
+ if (GenericUtils.isEmpty(providerName)) {
return Signature.getInstance(algorithm);
} else {
- return Signature.getInstance(algorithm, getSecurityProvider());
+ return Signature.getInstance(algorithm, providerName);
}
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/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
index 1051a69..0009a14 100644
--- 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
@@ -43,6 +43,12 @@ public final class ValidateUtils {
return t;
}
+ public static final 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 final String checkNotNullAndNotEmpty(String t, String message, Object ... args) {
t = checkNotNull(t, message, args).trim();
checkTrue(GenericUtils.length(t) > 0, message, args);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/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
index 9bae57e..73f7b59 100644
--- 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
@@ -27,6 +27,7 @@ import java.io.OutputStream;
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.attribute.PosixFilePermission;
import java.util.Arrays;
@@ -44,13 +45,13 @@ import org.apache.sshd.common.util.OsUtils;
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public class IoUtils {
-
- public static final LinkOption[] EMPTY_OPTIONS = new LinkOption[0];
+ public static final OpenOption[] EMPTY_OPEN_OPTIONS = new OpenOption[0];
+ public static final LinkOption[] EMPTY_LINK_OPTIONS = new LinkOption[0];
private static final LinkOption[] NO_FOLLOW_OPTIONS = new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
public static LinkOption[] getLinkOptions(boolean followLinks) {
if (followLinks) {
- return EMPTY_OPTIONS;
+ return EMPTY_LINK_OPTIONS;
} else { // return a clone that modifications to the array will not affect others
return NO_FOLLOW_OPTIONS.clone();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/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
index ddf0efb..c44cee0 100644
--- 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
@@ -56,7 +56,7 @@ public class ModifiableFileWatcher extends AbstractLoggingBean {
public ModifiableFileWatcher(Path file, LinkOption ... options) {
this.file = ValidateUtils.checkNotNull(file, "No path to watch", GenericUtils.EMPTY_OBJECT_ARRAY);
// use a clone to avoid being sensitive to changes in the passed array
- this.options = (options == null) ? IoUtils.EMPTY_OPTIONS : options.clone();
+ this.options = (options == null) ? IoUtils.EMPTY_LINK_OPTIONS : options.clone();
}
/**
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
index 3281b28..135ac0d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
@@ -18,6 +18,7 @@
*/
package org.apache.sshd.server;
+import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -50,7 +51,6 @@ import org.apache.sshd.server.auth.gss.GSSAuthenticator;
import org.apache.sshd.server.auth.gss.UserAuthGSS;
import org.apache.sshd.server.command.ScpCommandFactory;
import org.apache.sshd.server.forward.ForwardingFilter;
-import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.session.ServerConnectionService;
import org.apache.sshd.server.session.ServerSession;
@@ -428,10 +428,11 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
sshd.setPort(port);
if (SecurityUtils.isBouncyCastleRegistered()) {
- sshd.setKeyPairProvider(new PEMGeneratorHostKeyProvider("key.pem"));
+ sshd.setKeyPairProvider(SecurityUtils.createGeneratorHostKeyProvider(new File("key.pem").toPath()));
} else {
- sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("key.ser"));
+ sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(new File("key.ser")));
}
+
if (OsUtils.isUNIX()) {
sshd.setShellFactory(new ProcessShellFactory(new String[] { "/bin/sh", "-i", "-l" },
EnumSet.of(ProcessShellFactory.TtyOptions.ONlCr)));
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/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
index c6bafd3..87bc88b 100644
--- 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
@@ -19,57 +19,55 @@
package org.apache.sshd.server.keyprovider;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
+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.spec.AlgorithmParameterSpec;
import java.util.Collections;
import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
-import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.SecurityUtils;
+import org.apache.sshd.common.util.io.IoUtils;
/**
- * TODO Add javadoc
- *
+ * 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 = "DSA";
+ public static final boolean DEFAULT_ALLOWED_TO_OVERWRITE = true;
- private String path;
- private String algorithm;
+ private Path path;
+ private String algorithm = DEFAULT_ALGORITHM;
private int keySize;
private AlgorithmParameterSpec keySpec;
private KeyPair keyPair;
- private boolean overwriteAllowed = true;
+ private boolean overwriteAllowed = DEFAULT_ALLOWED_TO_OVERWRITE;
protected AbstractGeneratorHostKeyProvider() {
- this(null);
- }
-
- protected AbstractGeneratorHostKeyProvider(String path) {
- this(path, "DSA");
- }
-
- protected AbstractGeneratorHostKeyProvider(String path, String algorithm) {
- this(path, algorithm, 0);
+ super();
}
- protected AbstractGeneratorHostKeyProvider(String path, String algorithm, int keySize) {
- this.path = path;
- this.algorithm = algorithm;
- this.keySize = keySize;
+ public Path getPath() {
+ return path;
}
- public String getPath() {
- return path;
+ public void setFile(File file) {
+ setPath((file == null) ? null : file.toPath());
}
- public void setPath(String path) {
- this.path = path;
+ public void setPath(Path path) {
+ this.path = (path == null) ? null : path.toAbsolutePath();
}
public String getAlgorithm() {
@@ -104,25 +102,45 @@ public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairPr
this.overwriteAllowed = overwriteAllowed;
}
- protected abstract KeyPair doReadKeyPair(InputStream is) throws Exception;
-
- protected abstract void doWriteKeyPair(KeyPair kp, OutputStream os) throws Exception;
-
@Override
public synchronized Iterable<KeyPair> loadKeys() {
+ Path keyPath = getPath();
+
if (keyPair == null) {
- if (!GenericUtils.isEmpty(path)) {
- File f = new File(path);
- if (f.exists() && f.isFile()) {
- keyPair = readKeyPair(f);
+ if (keyPath != null) {
+ LinkOption[] options = IoUtils.getLinkOptions(false);
+ if (Files.exists(keyPath, options) && Files.isRegularFile(keyPath, options)) {
+ try {
+ keyPair = readKeyPair(keyPath, IoUtils.EMPTY_OPEN_OPTIONS);
+ } catch(Exception e) {
+ log.warn("Failed (" + e.getClass().getSimpleName() + ")"
+ + " to load from " + keyPath
+ + ": " + e.getMessage(),
+ e);
+ }
}
}
}
if (keyPair == null) {
- keyPair = generateKeyPair(getAlgorithm());
- if ((keyPair != null) && (!GenericUtils.isEmpty(path))) {
- writeKeyPair(keyPair, new File(path));
+ String alg = getAlgorithm();
+ try {
+ keyPair = generateKeyPair(alg);
+ } catch(Exception e) {
+ log.warn("Failed (" + e.getClass().getSimpleName() + ")"
+ + " to generate " + alg + " keys: " + e.getMessage(),
+ e);
+ }
+
+ if ((keyPair != null) && (keyPath != null)) {
+ try {
+ writeKeyPair(keyPair, keyPath);
+ } catch(Exception e) {
+ log.warn("Failed (" + e.getClass().getSimpleName() + ")"
+ + " to write to " + keyPath
+ + ": " + e.getMessage(),
+ e);
+ }
}
}
@@ -133,41 +151,37 @@ public abstract class AbstractGeneratorHostKeyProvider extends AbstractKeyPairPr
}
}
- private KeyPair readKeyPair(File f) {
- try(InputStream is = new FileInputStream(f)) {
- return doReadKeyPair(is);
- } catch (Exception e) {
- log.warn("Unable to read key {}: {}", f.getAbsolutePath(), e);
- 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 abstract KeyPair doReadKeyPair(String resourceKey, InputStream inputStream) throws IOException, GeneralSecurityException;
- private void writeKeyPair(KeyPair kp, File f) {
- if ((!f.exists()) || isOverwriteAllowed()) {
- try(OutputStream os = new FileOutputStream(f)) {
- doWriteKeyPair(kp, os);
+ 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 (Exception e) {
log.warn("Unable to write key {}: {}", path, e);
}
} else {
- log.error("Overwriting key ({}) is disabled: using throwaway {}", f.getAbsolutePath(), kp);
+ log.error("Overwriting key ({}) is disabled: using throwaway {}", keyPath, kp);
}
}
+ protected abstract void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException;
- private KeyPair generateKeyPair(String algorithm) {
- try {
- KeyPairGenerator generator = SecurityUtils.getKeyPairGenerator(algorithm);
- if (keySpec != null) {
- generator.initialize(keySpec);
- } else if (keySize != 0) {
- generator.initialize(keySize);
- }
- log.info("generateKeyPair(" + algorithm + ") generating host key...");
- KeyPair kp = generator.generateKeyPair();
- return kp;
- } catch (Exception e) {
- log.warn("generateKeyPair(" + algorithm + ") Unable to generate keypair", e);
- return null;
+
+ 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);
}
+
+ return generator.generateKeyPair();
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProvider.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProvider.java b/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProvider.java
deleted file mode 100644
index 47c884e..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/server/keyprovider/PEMGeneratorHostKeyProvider.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.server.keyprovider;
-
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.security.KeyPair;
-
-import org.bouncycastle.openssl.PEMKeyPair;
-import org.bouncycastle.openssl.PEMParser;
-import org.bouncycastle.openssl.PEMWriter;
-import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class PEMGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProvider {
-
- public PEMGeneratorHostKeyProvider() {
- super();
- }
-
- public PEMGeneratorHostKeyProvider(String path) {
- super(path);
- }
-
- public PEMGeneratorHostKeyProvider(String path, String algorithm) {
- super(path, algorithm);
- }
-
- public PEMGeneratorHostKeyProvider(String path, String algorithm, int keySize) {
- super(path, algorithm, keySize);
- }
-
- @Override
- protected KeyPair doReadKeyPair(InputStream is) throws Exception {
- try(PEMParser r = new PEMParser(new InputStreamReader(is))) {
- Object o = r.readObject();
- JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
- pemConverter.setProvider("BC");
- if (o instanceof PEMKeyPair) {
- o = pemConverter.getKeyPair((PEMKeyPair)o);
- return (KeyPair) o;
- } else if (o instanceof KeyPair) {
- return (KeyPair) o;
- }
- return null;
- }
- }
-
- @Override
- protected void doWriteKeyPair(KeyPair kp, OutputStream os) throws Exception {
- try(PEMWriter w = new PEMWriter(new OutputStreamWriter(os))) {
- w.writeObject(kp);
- w.flush();
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/06d37a2d/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
index 982d2e7..fff76a8 100644
--- 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
@@ -18,11 +18,16 @@
*/
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
@@ -30,33 +35,32 @@ import java.security.KeyPair;
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public class SimpleGeneratorHostKeyProvider extends AbstractGeneratorHostKeyProvider {
-
public SimpleGeneratorHostKeyProvider() {
super();
}
- public SimpleGeneratorHostKeyProvider(String path) {
- super(path);
- }
-
- public SimpleGeneratorHostKeyProvider(String path, String algorithm) {
- super(path, algorithm);
+ public SimpleGeneratorHostKeyProvider(File file) {
+ this((file == null) ? null : file.toPath());
}
- public SimpleGeneratorHostKeyProvider(String path, String algorithm, int keySize) {
- super(path, algorithm, keySize);
+ public SimpleGeneratorHostKeyProvider(Path path) {
+ setPath(path);
}
@Override
- protected KeyPair doReadKeyPair(InputStream is) throws Exception {
- try(ObjectInputStream r = new ObjectInputStream(is)) {
- return (KeyPair) r.readObject();
+ 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(KeyPair kp, OutputStream os) throws Exception {
- try(ObjectOutputStream w = new ObjectOutputStream(os)) {
+ protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream outputStream) throws IOException, GeneralSecurityException {
+ try(ObjectOutputStream w = new ObjectOutputStream(outputStream)) {
w.writeObject(kp);
}
}