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);
         }
     }