Repository: karaf Updated Branches: refs/heads/OPENSSH f7ec6fee5 -> 549471c74 (forced update)
[KARAF-5286] Separate server key generation from key reading. Only support openssh pem server keys Project: http://git-wip-us.apache.org/repos/asf/karaf/repo Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/549471c7 Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/549471c7 Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/549471c7 Branch: refs/heads/OPENSSH Commit: 549471c74cdf62b1bbcabc2bcb3857f22083f156 Parents: f7ea3e0 Author: Christian Schneider <[email protected]> Authored: Thu Aug 3 10:55:58 2017 +0200 Committer: Christian Schneider <[email protected]> Committed: Thu Aug 3 10:55:58 2017 +0200 ---------------------------------------------------------------------- shell/ssh/pom.xml | 1 - .../org/apache/karaf/shell/ssh/Activator.java | 42 +++++----- .../ssh/OpenSSHGeneratorFileKeyProvider.java | 82 -------------------- .../keygenerator/OpenSSHKeyPairGenerator.java | 54 +++++++++++++ .../keygenerator/OpenSSHKeyPairProvider.java | 56 +++++++++++++ .../karaf/shell/ssh/keygenerator/PemWriter.java | 47 +++++++++++ .../OpenSSHGeneratorKeyFileProviderTest.java | 62 --------------- .../OpenSSHGeneratorKeyFileProviderTest.java | 46 +++++++++++ 8 files changed, 223 insertions(+), 167 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/karaf/blob/549471c7/shell/ssh/pom.xml ---------------------------------------------------------------------- diff --git a/shell/ssh/pom.xml b/shell/ssh/pom.xml index abf5678..9c359f6 100644 --- a/shell/ssh/pom.xml +++ b/shell/ssh/pom.xml @@ -100,7 +100,6 @@ <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <version>${commons-io.version}</version> <scope>test</scope> </dependency> http://git-wip-us.apache.org/repos/asf/karaf/blob/549471c7/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java ---------------------------------------------------------------------- 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 8725079..a40eb9c 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 @@ -20,23 +20,27 @@ package org.apache.karaf.shell.ssh; import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.nio.file.Paths; +import java.security.KeyPair; import java.util.Collections; import org.apache.karaf.shell.api.action.lifecycle.Manager; import org.apache.karaf.shell.api.console.CommandLoggingFilter; import org.apache.karaf.shell.api.console.Session; import org.apache.karaf.shell.api.console.SessionFactory; +import org.apache.karaf.shell.ssh.keygenerator.OpenSSHKeyPairGenerator; +import org.apache.karaf.shell.ssh.keygenerator.OpenSSHKeyPairProvider; +import org.apache.karaf.shell.ssh.keygenerator.PemWriter; import org.apache.karaf.shell.support.RegexCommandLoggingFilter; import org.apache.karaf.util.tracker.BaseActivator; import org.apache.karaf.util.tracker.annotation.Managed; import org.apache.karaf.util.tracker.annotation.RequireService; import org.apache.karaf.util.tracker.annotation.Services; import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory; +import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.server.SshServer; import org.apache.sshd.server.forward.AcceptAllForwardingFilter; -import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider; -import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; import org.apache.sshd.server.scp.ScpCommandFactory; import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory; import org.osgi.framework.ServiceReference; @@ -143,7 +147,6 @@ public class Activator extends BaseActivator implements ManagedService { int nioWorkers = getInt("nio-workers", 2); String sshRealm = getString("sshRealm", "karaf"); String hostKey = getString("hostKey", System.getProperty("karaf.etc") + "/host.key"); - String hostKeyFormat = getString("hostKeyFormat", "simple"); String[] authMethods = getStringArray("authMethods", "keyboard-interactive,password,publickey"); int keySize = getInt("keySize", 4096); String algorithm = getString("algorithm", "RSA"); @@ -152,28 +155,14 @@ public class Activator extends BaseActivator implements ManagedService { String[] kexAlgorithms = getStringArray("kexAlgorithms", "diffie-hellman-group-exchange-sha256,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1"); String welcomeBanner = getString("welcomeBanner", null); String moduliUrl = getString("moduli-url", null); - - AbstractGeneratorHostKeyProvider keyPairProvider; - if ("simple".equalsIgnoreCase(hostKeyFormat)) { - keyPairProvider = new SimpleGeneratorHostKeyProvider(); - } else if ("PEM".equalsIgnoreCase(hostKeyFormat)) { - keyPairProvider = new OpenSSHGeneratorFileKeyProvider(); - } else { - LOGGER.error("Invalid host key format " + hostKeyFormat); - return null; - } - - keyPairProvider.setPath(Paths.get(hostKey)); - if (new File(hostKey).exists()) { - // do not trash key file if there's something wrong with it. - keyPairProvider.setOverwriteAllowed(false); - } else { - keyPairProvider.setKeySize(keySize); - keyPairProvider.setAlgorithm(algorithm); + + Path serverKeyPath = Paths.get(hostKey); + if (!serverKeyPath.toFile().exists()) { + createServerKey(serverKeyPath.toFile(), algorithm, keySize); } + KeyPairProvider keyPairProvider = new OpenSSHKeyPairProvider(serverKeyPath.toFile()); KarafJaasAuthenticator authenticator = new KarafJaasAuthenticator(sshRealm); - UserAuthFactoriesFactory authFactoriesFactory = new UserAuthFactoriesFactory(); authFactoriesFactory.setAuthMethods(authMethods); @@ -204,4 +193,13 @@ public class Activator extends BaseActivator implements ManagedService { return server; } + private void createServerKey(File keyFile, String algorithm, int keySize) { + try { + KeyPair kp = new OpenSSHKeyPairGenerator(algorithm, keySize).generate(); + new PemWriter(keyFile).writeKeyPair(algorithm, kp); + } catch (Exception e) { + throw new RuntimeException("Key file generation failed", e); + } + } + } http://git-wip-us.apache.org/repos/asf/karaf/blob/549471c7/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/OpenSSHGeneratorFileKeyProvider.java ---------------------------------------------------------------------- diff --git a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/OpenSSHGeneratorFileKeyProvider.java b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/OpenSSHGeneratorFileKeyProvider.java deleted file mode 100644 index dd17314..0000000 --- a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/OpenSSHGeneratorFileKeyProvider.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.karaf.shell.ssh; - -import org.apache.commons.ssl.PEMItem; -import org.apache.commons.ssl.PEMUtil; -import org.apache.commons.ssl.PKCS8Key; -import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.file.Paths; -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.util.ArrayList; -import java.util.Collection; - -/** - * Read SSH keys without resorting to BouncyCastle - */ -public class OpenSSHGeneratorFileKeyProvider extends AbstractGeneratorHostKeyProvider { - private String password; - - public OpenSSHGeneratorFileKeyProvider() { - } - - public OpenSSHGeneratorFileKeyProvider(String path) { - setPath(Paths.get(path)); - } - - public OpenSSHGeneratorFileKeyProvider(String path, String algorithm) { - this(path); - setAlgorithm(algorithm); - } - - public OpenSSHGeneratorFileKeyProvider(String path, String algorithm, int keySize) { - this(path, algorithm); - setKeySize(keySize); - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - @Override - protected KeyPair doReadKeyPair(String resourceKey, InputStream is) throws IOException, GeneralSecurityException { - PKCS8Key pkcs8 = new PKCS8Key(is, password == null ? null : password.toCharArray()); - return new KeyPair(pkcs8.getPublicKey(), pkcs8.getPrivateKey()); - } - - @Override - protected void doWriteKeyPair(String resourceKey, KeyPair kp, OutputStream os) throws IOException, GeneralSecurityException { - Collection<Object> items = new ArrayList<>(); - items.add(new PEMItem(kp.getPrivate().getEncoded(), "PRIVATE KEY")); - byte[] bytes = PEMUtil.encode(items); - os.write(bytes); - os.close(); - } - -} http://git-wip-us.apache.org/repos/asf/karaf/blob/549471c7/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/keygenerator/OpenSSHKeyPairGenerator.java ---------------------------------------------------------------------- diff --git a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/keygenerator/OpenSSHKeyPairGenerator.java b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/keygenerator/OpenSSHKeyPairGenerator.java new file mode 100644 index 0000000..38db81e --- /dev/null +++ b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/keygenerator/OpenSSHKeyPairGenerator.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.karaf.shell.ssh.keygenerator; + +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; + +import org.apache.sshd.common.cipher.ECCurves; +import org.apache.sshd.common.config.keys.KeyUtils; +import org.apache.sshd.common.util.security.SecurityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class OpenSSHKeyPairGenerator { + private Logger log = LoggerFactory.getLogger(this.getClass()); + private int keySize; + private String algorithm; + + public OpenSSHKeyPairGenerator(String algorithm, int keySize) { + this.algorithm = algorithm; + this.keySize = keySize; + } + + public KeyPair generate() throws GeneralSecurityException { + KeyPairGenerator generator = SecurityUtils.getKeyPairGenerator(algorithm); + if (keySize != 0) { + generator.initialize(keySize); + log.info("generateKeyPair(" + algorithm + ") generating host key - size=" + keySize); + } else if (KeyUtils.EC_ALGORITHM.equals(algorithm)) { + // If left to our own devices choose the biggest key size possible + int numCurves = ECCurves.SORTED_KEY_SIZE.size(); + ECCurves curve = ECCurves.SORTED_KEY_SIZE.get(numCurves - 1); + generator.initialize(curve.getParameters()); + } + return generator.generateKeyPair(); + } +} http://git-wip-us.apache.org/repos/asf/karaf/blob/549471c7/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/keygenerator/OpenSSHKeyPairProvider.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..43728a3 --- /dev/null +++ b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/keygenerator/OpenSSHKeyPairProvider.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.karaf.shell.ssh.keygenerator; + +import static java.util.Collections.singleton; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyPair; + +import org.apache.commons.ssl.PKCS8Key; +import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider; + +public class OpenSSHKeyPairProvider extends AbstractKeyPairProvider { + private File keyFile; + private String password; + + public OpenSSHKeyPairProvider(File keyFile) { + this.keyFile = keyFile; + } + + @Override + public Iterable<KeyPair> loadKeys() { + try (FileInputStream is = new FileInputStream(keyFile)) { + KeyPair kp = getKeyPair(is); + return singleton(kp); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private KeyPair getKeyPair(FileInputStream is) throws GeneralSecurityException, IOException { + PKCS8Key pkcs8 = new PKCS8Key(is, password == null ? null : password.toCharArray()); + KeyPair kp = new KeyPair(pkcs8.getPublicKey(), pkcs8.getPrivateKey()); + return kp; + } + +} http://git-wip-us.apache.org/repos/asf/karaf/blob/549471c7/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/keygenerator/PemWriter.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..960570d --- /dev/null +++ b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/keygenerator/PemWriter.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.karaf.shell.ssh.keygenerator; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.KeyPair; +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.commons.ssl.PEMItem; +import org.apache.commons.ssl.PEMUtil; + +public class PemWriter { + private File keyFile; + + public PemWriter(File keyFile) { + this.keyFile = keyFile; + } + + 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); + FileOutputStream os = new FileOutputStream(keyFile); + os.write(bytes); + os.close(); + } +} http://git-wip-us.apache.org/repos/asf/karaf/blob/549471c7/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/OpenSSHGeneratorKeyFileProviderTest.java ---------------------------------------------------------------------- diff --git a/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/OpenSSHGeneratorKeyFileProviderTest.java b/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/OpenSSHGeneratorKeyFileProviderTest.java deleted file mode 100644 index 04066c4..0000000 --- a/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/OpenSSHGeneratorKeyFileProviderTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.karaf.shell.ssh; - -import java.io.File; -import java.math.BigInteger; -import java.security.KeyPair; -import java.security.interfaces.RSAPrivateCrtKey; -import java.security.interfaces.RSAPublicKey; - -import org.junit.Assert; -import org.junit.Test; - -public class OpenSSHGeneratorKeyFileProviderTest { - - @Test - public void readSshKey() throws Exception { - OpenSSHGeneratorFileKeyProvider prov = new OpenSSHGeneratorFileKeyProvider("src/test/resources/org/apache/karaf/shell/ssh/test.pem"); - prov.setOverwriteAllowed(false); - KeyPair keys = prov.loadKeys().iterator().next(); - // how would we tell if they read 'correctly'? Well, the base class will throw if the key isn't reasonable. - Assert.assertNotNull(keys); - Assert.assertTrue("Loaded key is not RSA Key", keys.getPublic() instanceof RSAPublicKey); - Assert.assertEquals(65537, ((RSAPublicKey) keys.getPublic()).getPublicExponent().intValue()); - } - - @Test - public void writeSshKey() throws Exception { - // create a temporary file - File temp = File.createTempFile(this.getClass().getCanonicalName(), ".pem"); - temp.deleteOnExit(); - OpenSSHGeneratorFileKeyProvider prov = new OpenSSHGeneratorFileKeyProvider(temp.getPath(), "RSA", 4096); - KeyPair keys = prov.loadKeys().iterator().next(); - Assert.assertNotNull(keys); - Assert.assertTrue(temp.exists()); - Assert.assertFalse(temp.length() == 0); - BigInteger privateExponent = ((RSAPrivateCrtKey) keys.getPrivate()).getPrivateExponent(); - // read and check if correctly read - prov = new OpenSSHGeneratorFileKeyProvider(temp.getPath()); - keys = prov.loadKeys().iterator().next(); - Assert.assertNotNull(keys); - Assert.assertTrue("Loaded key is not RSA Key", keys.getPrivate() instanceof RSAPrivateCrtKey); - BigInteger privateExponent2 = ((RSAPrivateCrtKey) keys.getPrivate()).getPrivateExponent(); - Assert.assertEquals(privateExponent, privateExponent2); - } -} http://git-wip-us.apache.org/repos/asf/karaf/blob/549471c7/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/keygenerator/OpenSSHGeneratorKeyFileProviderTest.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..891b042 --- /dev/null +++ b/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/keygenerator/OpenSSHGeneratorKeyFileProviderTest.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.karaf.shell.ssh.keygenerator; + +import java.io.File; +import java.security.KeyPair; +import java.security.interfaces.RSAPrivateCrtKey; + +import org.apache.sshd.common.config.keys.KeyUtils; +import org.junit.Assert; +import org.junit.Test; + +public class OpenSSHGeneratorKeyFileProviderTest { + + @Test + public void writeSshKey() throws Exception { + File temp = File.createTempFile(this.getClass().getCanonicalName(), ".pem"); + temp.deleteOnExit(); + + KeyPair kp = new OpenSSHKeyPairGenerator(KeyUtils.RSA_ALGORITHM, 1024).generate(); + new PemWriter(temp).writeKeyPair(KeyUtils.RSA_ALGORITHM, kp); + + //File path = new File("/home/cschneider/.ssh/id_rsa"); + OpenSSHKeyPairProvider prov = new OpenSSHKeyPairProvider(temp); + KeyPair keys = prov.loadKeys().iterator().next(); + Assert.assertNotNull(keys); + Assert.assertTrue("Loaded key is not RSA Key", keys.getPrivate() instanceof RSAPrivateCrtKey); + } + +}
