You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2019/06/22 05:09:27 UTC

[karaf] branch master updated: Adding support for SSH using elliptic curves

This is an automated email from the ASF dual-hosted git repository.

jbonofre pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/karaf.git


The following commit(s) were added to refs/heads/master by this push:
     new 1d057cf  Adding support for SSH using elliptic curves
     new d18833c  Merge pull request #886 from coheigea/ssh_ec
1d057cf is described below

commit 1d057cfadf3af589b687beea2fbfda2ea26e5ace
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Thu Jun 20 16:59:16 2019 +0100

    Adding support for SSH using elliptic curves
---
 .../features/standard/src/main/feature/feature.xml |  8 +-
 manual/src/main/asciidoc/user-guide/remote.adoc    | 12 ++-
 .../java/org/apache/karaf/shell/ssh/Activator.java |  8 +-
 .../karaf/shell/ssh/KarafJaasAuthenticator.java    |  1 -
 .../org/apache/karaf/shell/ssh/SshTerminal.java    |  1 -
 .../ssh/keygenerator/OpenSSHKeyPairProvider.java   | 92 ++++++++++++++++------
 .../karaf/shell/ssh/keygenerator/PemWriter.java    | 25 ++++--
 .../OSGI-INF/metatype/metatype.properties          |  9 ++-
 .../main/resources/OSGI-INF/metatype/metatype.xml  |  1 +
 .../OpenSSHGeneratorKeyFileProviderTest.java       | 45 ++++++++---
 10 files changed, 151 insertions(+), 51 deletions(-)

diff --git a/assemblies/features/standard/src/main/feature/feature.xml b/assemblies/features/standard/src/main/feature/feature.xml
index 9648856..8aeacb3 100644
--- a/assemblies/features/standard/src/main/feature/feature.xml
+++ b/assemblies/features/standard/src/main/feature/feature.xml
@@ -287,12 +287,18 @@ sshRole = ssh
 sftpEnabled=true
 
 #
-# The location of the hostKey file defines where the private/public key of the server
+# The location of the hostKey file defines where the private key of the server
 # is located. If no file is at the defined location it will be ignored.
 #
 hostKey = ${karaf.etc}/host.key
 
 #
+# The location of the hostKeyPub file defines where the public key of the server
+# is located. If no file is at the defined location it will be ignored.
+#
+#hostKeyPub = ${karaf.etc}/host.key.pub
+
+#
 # Self defined key size in 1024, 2048, 3072, or 4096
 # If not set, this defaults to 2048.
 #
diff --git a/manual/src/main/asciidoc/user-guide/remote.adoc b/manual/src/main/asciidoc/user-guide/remote.adoc
index 3fa6178..4deed60 100644
--- a/manual/src/main/asciidoc/user-guide/remote.adoc
+++ b/manual/src/main/asciidoc/user-guide/remote.adoc
@@ -76,12 +76,18 @@ sshIdleTimeout = 1800000
 sshRealm = karaf
 
 #
-# The location of the hostKey file defines where the private/public key of the server
+# The location of the hostKey file defines where the private key of the server
 # is located. If no file is at the defined location it will be ignored.
 #
 hostKey = ${karaf.etc}/host.key
 
 #
+# The location of the hostKeyPub file defines where the public key of the server
+# is located. If no file is at the defined location it will be ignored.
+#
+#hostKeyPub = ${karaf.etc}/host.key.pub
+
+#
 # Role name used for SSH access authorization
 #
 # sshRole = admin
@@ -122,8 +128,8 @@ The `etc/org.apache.karaf.shell.cfg` configuration file contains different prope
 * `sshHost` is the address of the network interface where the SSHd server is bound. The default value is 0.0.0.0,
  meaning that the SSHd server is bound on all network interfaces. You can bind on a target interface providing the IP
  address of the network interface.
-* `hostKey` is the location of the `host.key` file. By defaut, it uses `etc/host.key`. This file stores the public
- and private key pair of the SSHd server.
+* `hostKey` is the location of the `host.key` file. By defaut, it uses `etc/host.key`. This file stores the 
+ private key of the SSHd server.
 * `sshRole` is the default role used for SSH access. See the [Security section|security] of this user guide for details.
 * `sftpEnabled` controls if the SSH server start the SFTP system or not. When enabled, Karaf SSHd supports SFTP, meaning
  that you can remotely access to the Karaf filesystem with any sftp clients.
diff --git a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java
index 31e5fab..88772d0 100644
--- a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java
+++ b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java
@@ -149,7 +149,8 @@ public class Activator extends BaseActivator implements ManagedService {
         String sshRealm             = getString("sshRealm", "karaf");
         Class<?>[] roleClasses      = getClassesArray("sshRoleTypes", "org.apache.karaf.jaas.boot.principal.RolePrincipal");
         String sshRole              = getString("sshRole", null);
-        String hostKey              = getString("hostKey", System.getProperty("karaf.etc") + "/host.key");
+        String privateHostKey       = getString("hostKey", System.getProperty("karaf.etc") + "/host.key");
+        String publicHostKey        = getString("hostKeyPublic", System.getProperty("karaf.etc") + "/host.key.pub");
         String[] authMethods        = getStringArray("authMethods", "keyboard-interactive,password,publickey");
         int keySize                 = getInt("keySize", 2048);
         String algorithm            = getString("algorithm", "RSA");
@@ -160,8 +161,9 @@ public class Activator extends BaseActivator implements ManagedService {
         String moduliUrl            = getString("moduli-url", null);
         boolean sftpEnabled         = getBoolean("sftpEnabled", true);
         
-        Path serverKeyPath = Paths.get(hostKey);
-        KeyPairProvider keyPairProvider = new OpenSSHKeyPairProvider(serverKeyPath.toFile(), algorithm, keySize);
+        Path serverPrivateKeyPath = Paths.get(privateHostKey);
+        Path serverPublicKeyPath = Paths.get(publicHostKey);
+        KeyPairProvider keyPairProvider = new OpenSSHKeyPairProvider(serverPrivateKeyPath, serverPublicKeyPath, algorithm, keySize);
         KarafJaasAuthenticator authenticator = new KarafJaasAuthenticator(sshRealm, sshRole, roleClasses);
         UserAuthFactoriesFactory authFactoriesFactory = new UserAuthFactoriesFactory();
         authFactoriesFactory.setAuthMethods(authMethods);
diff --git a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/KarafJaasAuthenticator.java b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/KarafJaasAuthenticator.java
index 34e34ec..1f01e6c 100644
--- a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/KarafJaasAuthenticator.java
+++ b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/KarafJaasAuthenticator.java
@@ -31,7 +31,6 @@ import javax.security.auth.login.FailedLoginException;
 import javax.security.auth.login.LoginContext;
 
 import org.apache.karaf.jaas.boot.principal.ClientPrincipal;
-import org.apache.karaf.jaas.boot.principal.RolePrincipal;
 import org.apache.karaf.jaas.modules.publickey.PublickeyCallback;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.server.auth.password.PasswordAuthenticator;
diff --git a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshTerminal.java b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshTerminal.java
index a166485..a002d6e 100644
--- a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshTerminal.java
+++ b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshTerminal.java
@@ -22,7 +22,6 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
 import java.util.EnumSet;
 import java.util.Map;
 
diff --git a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/keygenerator/OpenSSHKeyPairProvider.java b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/keygenerator/OpenSSHKeyPairProvider.java
index 0e6265b..0e16d2e 100644
--- a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/keygenerator/OpenSSHKeyPairProvider.java
+++ b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/keygenerator/OpenSSHKeyPairProvider.java
@@ -20,13 +20,22 @@ package org.apache.karaf.shell.ssh.keygenerator;
 
 import static java.util.Collections.singleton;
 
-import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.ObjectInputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
 import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
 import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
 
 import org.apache.commons.ssl.PKCS8Key;
 import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider;
@@ -35,14 +44,16 @@ import org.slf4j.LoggerFactory;
 
 public class OpenSSHKeyPairProvider extends AbstractKeyPairProvider {
     private static final Logger LOGGER = LoggerFactory.getLogger(OpenSSHKeyPairProvider.class);
-    private File keyFile;
+    private Path privateKeyPath;
+    private Path publicKeyPath;
     private String password;
     private KeyPair cachedKey;
     private String algorithm;
     private int keySize;
 
-    public OpenSSHKeyPairProvider(File keyFile, String algorithm, int keySize) {
-        this.keyFile = keyFile;
+    public OpenSSHKeyPairProvider(Path privateKeyPath, Path publicKeyPath, String algorithm, int keySize) {
+        this.privateKeyPath = privateKeyPath;
+        this.publicKeyPath = publicKeyPath;
         this.algorithm = algorithm;
         this.keySize = keySize;
     }
@@ -52,51 +63,86 @@ public class OpenSSHKeyPairProvider extends AbstractKeyPairProvider {
         if (cachedKey != null) {
             return singleton(cachedKey);
         }
-        if (!keyFile.exists()) {
+        if (!privateKeyPath.toFile().exists()) {
             createServerKey();
         }
-        try (FileInputStream is = new FileInputStream(keyFile)) {
+
+        // 1. Try to read the PKCS8 private key. If it is RSA or DSA we can infer the public key directly from the
+        // private key, so there is no need to load the public key.
+        try (InputStream is = Files.newInputStream(privateKeyPath)) {
             KeyPair kp = getKeyPair(is);
             cachedKey = kp;
             return singleton(kp);
         } catch (Exception e) {
-            LOGGER.warn("Failed to parse keypair in {}. Attempting to parse it as a legacy 'simple' key", keyFile);
+            // 2. Failed to parse PKCS8 private key. Try to parse it directly and use the public key to create a KeyPair
+            // This is what will happen if it is an elliptic curve key for example
+            LOGGER.warn("Failed to parse keypair in {}. Attempting to parse it 'directly'", privateKeyPath);
             try {
-                KeyPair kp = convertLegacyKey(keyFile);
-                LOGGER.info("Successfully loaded legacy simple key. Converted to PEM format");
+                KeyPair kp = getKeyPairUsingPublicKeyFile();
+                LOGGER.info("Successfully loaded key pair");
                 cachedKey = kp;
-                return singleton(kp);
-            } catch (Exception nested) {
-                LOGGER.warn(keyFile+" is not a 'simple' key either",nested);
+                return singleton(cachedKey);
+            } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException | IllegalArgumentException e1) {
+                // 3. On a failure, see if we are dealing with a "legacy" keypair.
+                LOGGER.warn("Failed to parse keypair in {}. Attempting to parse it as a legacy 'simple' key", privateKeyPath);
+                try {
+                    KeyPair kp = convertLegacyKey(privateKeyPath);
+                    LOGGER.info("Successfully loaded legacy simple key. Converted to PEM format");
+                    cachedKey = kp;
+                    return singleton(kp);
+                } catch (Exception nested) {
+                    LOGGER.warn(privateKeyPath + " is not a 'simple' key either", nested);
+                }
             }
             throw new RuntimeException(e);
         }
     }
 
-    private KeyPair getKeyPair(FileInputStream is) throws GeneralSecurityException, IOException {
+    private KeyPair getKeyPair(InputStream is) throws GeneralSecurityException, IOException {
         PKCS8Key pkcs8 = new PKCS8Key(is, password == null ? null : password.toCharArray());
-        KeyPair kp = new KeyPair(pkcs8.getPublicKey(), pkcs8.getPrivateKey());
-        return kp;
+        return new KeyPair(pkcs8.getPublicKey(), pkcs8.getPrivateKey());
     }
 
-
-    private KeyPair convertLegacyKey(File keyFile) throws GeneralSecurityException, IOException {
+    private KeyPair convertLegacyKey(Path privateKeyPath) throws GeneralSecurityException, IOException {
         KeyPair keypair = null;
-        try (ObjectInputStream r = new ObjectInputStream(new FileInputStream(keyFile))) {
+        try (ObjectInputStream r = new ObjectInputStream(Files.newInputStream(privateKeyPath))) {
             keypair = (KeyPair)r.readObject();
         }
         catch (ClassNotFoundException e) {
             throw new InvalidKeySpecException("Missing classes: " + e.getMessage(), e);
         }
-        new PemWriter(keyFile).writeKeyPair(algorithm, keypair);
+        new PemWriter(privateKeyPath, publicKeyPath).writeKeyPair(algorithm, keypair);
         return keypair;
     }
 
-    private void createServerKey() {
+    private KeyPair getKeyPairUsingPublicKeyFile() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
+        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
+
+        // Read private key
+        String content = new String(Files.readAllBytes(privateKeyPath), StandardCharsets.UTF_8);
+        content = content.replace("-----BEGIN PRIVATE KEY-----", "");
+        content = content.replace("-----END PRIVATE KEY-----", "");
+
+        PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(Base64.getMimeDecoder().decode(content));
+        PrivateKey privateKey = keyFactory.generatePrivate(encodedKeySpec);
+
+        // Read public key
+        content = new String(Files.readAllBytes(publicKeyPath), StandardCharsets.UTF_8);
+        content = content.replace("-----BEGIN PUBLIC KEY-----", "");
+        content = content.replace("-----END PUBLIC KEY-----", "");
+
+        X509EncodedKeySpec encodedX509KeySpec = new X509EncodedKeySpec(Base64.getMimeDecoder().decode(content));
+        PublicKey publicKey = keyFactory.generatePublic(encodedX509KeySpec);
+
+        return new KeyPair(publicKey, privateKey);
+    }
+
+    private KeyPair createServerKey() {
         try {
-            LOGGER.info("Creating ssh server key at " + keyFile);
+            LOGGER.info("Creating ssh server private key at " + privateKeyPath);
             KeyPair kp = new OpenSSHKeyPairGenerator(algorithm, keySize).generate();
-            new PemWriter(keyFile).writeKeyPair(algorithm, kp);
+            new PemWriter(privateKeyPath, publicKeyPath).writeKeyPair(algorithm, kp);
+            return kp;
         } catch (Exception e) {
             throw new RuntimeException("Key file generation failed", e);
         }
diff --git a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/keygenerator/PemWriter.java b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/keygenerator/PemWriter.java
index 078a125..66755c4 100644
--- a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/keygenerator/PemWriter.java
+++ b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/keygenerator/PemWriter.java
@@ -18,10 +18,11 @@
  */
 package org.apache.karaf.shell.ssh.keygenerator;
 
-import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.security.KeyPair;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -30,17 +31,27 @@ import org.apache.commons.ssl.PEMItem;
 import org.apache.commons.ssl.PEMUtil;
 
 public class PemWriter {
-    private File keyFile;
+    private Path privateKeyPath;
+    private Path publicKeyPath;
 
-    public PemWriter(File keyFile) {
-        this.keyFile = keyFile;
+    public PemWriter(Path privateKeyPath, Path publicKeyPath) {
+        this.privateKeyPath = privateKeyPath;
+        this.publicKeyPath = publicKeyPath;
     }
-    
+
     public void writeKeyPair(String resource, KeyPair kp) throws IOException, FileNotFoundException {
         Collection<Object> items = new ArrayList<>();
+
         items.add(new PEMItem(kp.getPrivate().getEncoded(), "PRIVATE KEY"));
         byte[] bytes = PEMUtil.encode(items);
-        try (FileOutputStream os = new FileOutputStream(keyFile)) {
+        try (OutputStream os = Files.newOutputStream(privateKeyPath)) {
+            os.write(bytes);
+        }
+
+        items.clear();
+        items.add(new PEMItem(kp.getPublic().getEncoded(), "PUBLIC KEY"));
+        bytes = PEMUtil.encode(items);
+        try (OutputStream os = Files.newOutputStream(publicKeyPath)) {
             os.write(bytes);
         }
     }
diff --git a/shell/ssh/src/main/resources/OSGI-INF/metatype/metatype.properties b/shell/ssh/src/main/resources/OSGI-INF/metatype/metatype.properties
index c4090b5..235513f 100644
--- a/shell/ssh/src/main/resources/OSGI-INF/metatype/metatype.properties
+++ b/shell/ssh/src/main/resources/OSGI-INF/metatype/metatype.properties
@@ -33,11 +33,14 @@ sshHost.description = name of the host used to bind the SSH daemon
 sshRealm.name = SSH Realm
 sshRealm.description = name of the JAAS realm to use for SSH authentication
 
-hostKey.name = Host key
-hostKey.description = location of the host key for SSH
+hostKey.name = Private host key
+hostKey.description = location of the private host key for SSH
+
+hostKeyPub.name = Public host key
+hostKeyPub.description = location of the public host key for SSH
 
 keySize.name = Key size
 keySize.description = Secret key size in 1024, 2048, 3072, or 4096
 
 algorithm.name = Key algorithm
-algorithm.description = Host key algorithm in DSA, RSA, etc
\ No newline at end of file
+algorithm.description = Host key algorithm in DSA, RSA, EC, etc
\ No newline at end of file
diff --git a/shell/ssh/src/main/resources/OSGI-INF/metatype/metatype.xml b/shell/ssh/src/main/resources/OSGI-INF/metatype/metatype.xml
index dbb7a02..e52eeb1 100644
--- a/shell/ssh/src/main/resources/OSGI-INF/metatype/metatype.xml
+++ b/shell/ssh/src/main/resources/OSGI-INF/metatype/metatype.xml
@@ -23,6 +23,7 @@
         <AD id="sshHost" type="String" default="0.0.0.0" name="%sshHost.name" description="%sshHost.description"/>
         <AD id="sshRealm" type="String" default="karaf" name="%sshRealm.name" description="%sshRealm.description"/>
         <AD id="hostKey" type="String" default="${karaf.etc}/host.key" name="%hostKey.name" description="%hostKey.description"/>
+        <AD id="hostKeyPublic" type="String" default="${karaf.etc}/host.key.pub" name="%hostKeyPublic.name" description="%hostKeyPublic.description"/>
         <AD id="keySize" type="Integer" default="2048" name="%keySize.name" description="%keySize.description"/>
         <AD id="algorithm" type="String" default="RSA" name="%algorithm.name" description="%algorithm.description"/>
     </OCD>
diff --git a/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/keygenerator/OpenSSHGeneratorKeyFileProviderTest.java b/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/keygenerator/OpenSSHGeneratorKeyFileProviderTest.java
index c6d7d19..8034e9d 100644
--- a/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/keygenerator/OpenSSHGeneratorKeyFileProviderTest.java
+++ b/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/keygenerator/OpenSSHGeneratorKeyFileProviderTest.java
@@ -21,7 +21,10 @@ package org.apache.karaf.shell.ssh.keygenerator;
 import java.io.File;
 import java.nio.file.Files;
 import java.security.KeyPair;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
 import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPublicKey;
 import java.util.List;
 
 import org.apache.commons.ssl.PKCS8Key;
@@ -34,25 +37,31 @@ public class OpenSSHGeneratorKeyFileProviderTest {
 
     @Test
     public void writeSshKey() throws Exception {
-        File temp = File.createTempFile(this.getClass().getCanonicalName(), ".pem");
-        temp.deleteOnExit();
+        File privateKeyTemp = File.createTempFile(this.getClass().getCanonicalName(), ".priv");
+        privateKeyTemp.deleteOnExit();
+        File publicKeyTemp = File.createTempFile(this.getClass().getCanonicalName(), ".pub");
+        publicKeyTemp.deleteOnExit();
 
         KeyPair kp = new OpenSSHKeyPairGenerator(KeyUtils.RSA_ALGORITHM, 1024).generate();
-        new PemWriter(temp).writeKeyPair(KeyUtils.RSA_ALGORITHM, kp);
+        new PemWriter(privateKeyTemp.toPath(), publicKeyTemp.toPath()).writeKeyPair(KeyUtils.RSA_ALGORITHM, kp);
 
         //File path = new File("/home/cschneider/.ssh/id_rsa");
-        OpenSSHKeyPairProvider prov = new OpenSSHKeyPairProvider(temp, KeyUtils.RSA_ALGORITHM, 1024);
+        OpenSSHKeyPairProvider prov =
+            new OpenSSHKeyPairProvider(privateKeyTemp.toPath(), publicKeyTemp.toPath(), KeyUtils.RSA_ALGORITHM, 1024);
         KeyPair keys = prov.loadKeys().iterator().next();
         Assert.assertNotNull(keys);
         Assert.assertTrue("Loaded key is not RSA Key", keys.getPrivate() instanceof RSAPrivateCrtKey);
+        Assert.assertTrue("Loaded key is not RSA Key", keys.getPublic() instanceof RSAPublicKey);
     }
 
     @Test
     public void convertSimpleKey() throws Exception {
-        File temp = File.createTempFile(this.getClass().getCanonicalName(), ".pem");
-        temp.deleteOnExit();
+        File privateKeyTemp = File.createTempFile(this.getClass().getCanonicalName(), ".priv");
+        privateKeyTemp.deleteOnExit();
+        File publicKeyTemp = File.createTempFile(this.getClass().getCanonicalName(), ".pub");
+        publicKeyTemp.deleteOnExit();
 
-        SimpleGeneratorHostKeyProvider simpleGenerator = new SimpleGeneratorHostKeyProvider(temp);
+        SimpleGeneratorHostKeyProvider simpleGenerator = new SimpleGeneratorHostKeyProvider(privateKeyTemp);
         simpleGenerator.setKeySize(2048);
         simpleGenerator.setAlgorithm("DSA");
         List<KeyPair> keys = simpleGenerator.loadKeys();
@@ -60,7 +69,8 @@ public class OpenSSHGeneratorKeyFileProviderTest {
 
         Assert.assertEquals("DSA", simpleKeyPair.getPrivate().getAlgorithm());
 
-        OpenSSHKeyPairProvider provider = new OpenSSHKeyPairProvider(temp, "DSA", 2048);
+        OpenSSHKeyPairProvider provider = 
+            new OpenSSHKeyPairProvider(privateKeyTemp.toPath(), publicKeyTemp.toPath(), "DSA", 2048);
         KeyPair convertedKeyPair = provider.loadKeys().iterator().next();
         Assert.assertEquals("DSA", convertedKeyPair.getPrivate().getAlgorithm());
 
@@ -68,10 +78,27 @@ public class OpenSSHGeneratorKeyFileProviderTest {
         Assert.assertArrayEquals(simpleKeyPair.getPublic().getEncoded(),convertedKeyPair.getPublic().getEncoded());
 
         //also test that the original file has been replaced
-        PKCS8Key pkcs8 = new PKCS8Key(Files.newInputStream(temp.toPath()), null );
+        PKCS8Key pkcs8 = new PKCS8Key(Files.newInputStream(privateKeyTemp.toPath()), null );
         KeyPair keyPair = new KeyPair(pkcs8.getPublicKey(), pkcs8.getPrivateKey());
         Assert.assertArrayEquals(simpleKeyPair.getPrivate().getEncoded(),keyPair.getPrivate().getEncoded());
+    }
+
+    @Test
+    public void writeECSshKey() throws Exception {
+        File privateKeyTemp = File.createTempFile(this.getClass().getCanonicalName(), ".priv");
+        privateKeyTemp.deleteOnExit();
+        File publicKeyTemp = File.createTempFile(this.getClass().getCanonicalName(), ".pub");
+        publicKeyTemp.deleteOnExit();
 
+        KeyPair kp = new OpenSSHKeyPairGenerator(KeyUtils.EC_ALGORITHM, 256).generate();
+        new PemWriter(privateKeyTemp.toPath(), publicKeyTemp.toPath()).writeKeyPair(KeyUtils.EC_ALGORITHM, kp);
+
+        OpenSSHKeyPairProvider prov =
+            new OpenSSHKeyPairProvider(privateKeyTemp.toPath(), publicKeyTemp.toPath(), KeyUtils.EC_ALGORITHM, 256);
+        KeyPair keys = prov.loadKeys().iterator().next();
+        Assert.assertNotNull(keys);
+        Assert.assertTrue("Loaded key is not EC Key", keys.getPrivate() instanceof ECPrivateKey);
+        Assert.assertTrue("Loaded key is not EC Key", keys.getPublic() instanceof ECPublicKey);
     }
 
 }