You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by cs...@apache.org on 2012/06/29 15:51:18 UTC
svn commit: r1355385 - in /karaf/trunk/shell/ssh/src:
main/java/org/apache/karaf/shell/ssh/ main/resources/OSGI-INF/blueprint/
test/java/org/apache/karaf/shell/ssh/
Author: cschneider
Date: Fri Jun 29 13:51:16 2012
New Revision: 1355385
URL: http://svn.apache.org/viewvc?rev=1355385&view=rev
Log:
KARAF-1506 Adding store and verifier for host keys
Added:
karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/KnownHostsManager.java
karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/ServerKeyVerifierImpl.java
karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/KnownHostsManagerTest.java
karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/ServerKeyVerifierImplTest.java
Modified:
karaf/trunk/shell/ssh/src/main/resources/OSGI-INF/blueprint/shell-ssh.xml
Added: karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/KnownHostsManager.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/KnownHostsManager.java?rev=1355385&view=auto
==============================================================================
--- karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/KnownHostsManager.java (added)
+++ karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/KnownHostsManager.java Fri Jun 29 13:51:16 2012
@@ -0,0 +1,142 @@
+/*
+ * 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.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+
+import org.apache.mina.util.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class KnownHostsManager {
+ Logger LOG = LoggerFactory.getLogger(KnownHostsManager.class);
+
+ private final File knownHosts;
+
+ public KnownHostsManager(File knownHosts) {
+ this.knownHosts = knownHosts;
+ this.knownHosts.getParentFile().mkdirs();
+ if (!this.knownHosts.exists()) {
+ try {
+ knownHosts.createNewFile();
+ } catch (IOException e) {
+ throw new RuntimeException("Error creating file for known hosts at: " + knownHosts);
+ }
+ }
+ }
+
+ public PublicKey getKnownKey(SocketAddress remoteAddress, String checkAlgorithm) throws InvalidKeySpecException {
+ FileReader fr = null;
+ BufferedReader reader = null;
+ try {
+ fr = new FileReader(knownHosts);
+ reader = new BufferedReader(fr);
+ return getKnownKeyInternal(remoteAddress, checkAlgorithm, reader);
+ } catch (IOException e) {
+ throw new RuntimeException("Error reading known_hosts " + knownHosts, e);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ } finally {
+ close(reader);
+ close(fr);
+ }
+ }
+
+ private PublicKey getKnownKeyInternal(SocketAddress remoteAddress,
+ String checkAlgorithm, BufferedReader reader) throws IOException,
+ NoSuchAlgorithmException, InvalidKeySpecException {
+ String checkServerAddress = getAddressString(remoteAddress);
+
+ String line = reader.readLine();
+ while (line != null) {
+ String[] lineParts = line.split(" ");
+ String serverAddress = lineParts[0];
+ String algorithm = lineParts[1];
+ if (checkServerAddress.equals(serverAddress) && checkAlgorithm.equals(algorithm)) {
+ byte[] key = Base64.decodeBase64(lineParts[2].getBytes());
+ KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(key);
+ return keyFactory.generatePublic(keySpec);
+ }
+ line = reader.readLine();
+ }
+ return null;
+ }
+
+ public void storeKeyForHost(SocketAddress remoteAddress,
+ PublicKey serverKey) {
+ FileWriter ps = null;
+ BufferedWriter bw = null;
+ try {
+ ps = new FileWriter(knownHosts, true);
+ bw = new BufferedWriter(ps);
+ writeKey(bw, remoteAddress, serverKey);
+ } catch (Exception e) {
+ throw new RuntimeException("Error storing key for host" + remoteAddress, e);
+ } finally {
+ close(bw);
+ close(ps);
+ }
+ }
+
+ private void writeKey(BufferedWriter bw, SocketAddress remoteAddress,
+ PublicKey serverKey) throws IOException {
+ bw.append(getAddressString(remoteAddress));
+ bw.append(" ");
+ bw.append(serverKey.getAlgorithm());
+ bw.append(" ");
+ serverKey.getEncoded();
+ bw.append(new String(Base64.encodeBase64(serverKey.getEncoded()),
+ "UTF-8"));
+ }
+
+ String getAddressString(SocketAddress address) {
+ if (address instanceof InetSocketAddress) {
+ InetSocketAddress inetAddress = (InetSocketAddress) address;
+ return String.format("%s,%s:%s", inetAddress.getHostName(),
+ inetAddress.getAddress().getHostAddress(),
+ inetAddress.getPort());
+ }
+ return "";
+ }
+
+ private void close(Closeable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (IOException e) {
+ LOG.warn("Error closing: " + e.getMessage(), e);
+ }
+ }
+ }
+
+}
Added: karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/ServerKeyVerifierImpl.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/ServerKeyVerifierImpl.java?rev=1355385&view=auto
==============================================================================
--- karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/ServerKeyVerifierImpl.java (added)
+++ karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/ServerKeyVerifierImpl.java Fri Jun 29 13:51:16 2012
@@ -0,0 +1,61 @@
+/*
+ * 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.net.SocketAddress;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.client.ServerKeyVerifier;
+
+public class ServerKeyVerifierImpl implements ServerKeyVerifier {
+ private final KnownHostsManager knownHostsManager;
+
+ public ServerKeyVerifierImpl(KnownHostsManager knownHostsManager) {
+ this.knownHostsManager = knownHostsManager;
+
+ }
+
+ @Override
+ public boolean verifyServerKey(ClientSession sshClientSession,
+ SocketAddress remoteAddress, PublicKey serverKey) {
+ PublicKey knownKey;
+ try {
+ knownKey = knownHostsManager.getKnownKey(remoteAddress, serverKey.getAlgorithm());
+ } catch (InvalidKeySpecException e) {
+ System.out.println("Invalid key stored for host " + remoteAddress + ". Terminating session.");
+ return false;
+ }
+ if (knownKey == null) {
+ System.out.println("Connecting to this server for the first time. Storing the server key.");
+ knownHostsManager.storeKeyForHost(remoteAddress, serverKey);
+ return true;
+ }
+
+ boolean verifed = (knownKey.equals(serverKey));
+ if (!verifed) {
+ System.out.println("Server key for host " + remoteAddress + " does not match the stored key !! Terminating session.");
+ }
+ return verifed;
+ }
+
+
+
+}
Modified: karaf/trunk/shell/ssh/src/main/resources/OSGI-INF/blueprint/shell-ssh.xml
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/ssh/src/main/resources/OSGI-INF/blueprint/shell-ssh.xml?rev=1355385&r1=1355384&r2=1355385&view=diff
==============================================================================
--- karaf/trunk/shell/ssh/src/main/resources/OSGI-INF/blueprint/shell-ssh.xml (original)
+++ karaf/trunk/shell/ssh/src/main/resources/OSGI-INF/blueprint/shell-ssh.xml Fri Jun 29 13:51:16 2012
@@ -69,9 +69,18 @@
</action>
</command>
</command-bundle>
+
+ <bean id="knownHostsManager" class="org.apache.karaf.shell.ssh.KnownHostsManager">
+ <argument value="$[user.home]/.sshkaraf/known_hosts"/>
+ </bean>
+
+ <bean id="serverKeyVerifier" class="org.apache.karaf.shell.ssh.ServerKeyVerifierImpl">
+ <argument ref="knownHostsManager"/>
+ </bean>
<bean id="sshClient" class="org.apache.sshd.SshClient" factory-method="setUpDefaultClient" scope="prototype">
<property name="agentFactory" ref="agentFactory" />
+ <property name="serverKeyVerifier" ref="serverKeyVerifier" />
</bean>
<bean id="userAuthFactoriesFactory" class="org.apache.karaf.shell.ssh.UserAuthFactoriesFactory">
@@ -133,4 +142,4 @@
<reference id="commandProcessor" interface="org.apache.felix.service.command.CommandProcessor">
</reference>
-</blueprint>
\ No newline at end of file
+</blueprint>
Added: karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/KnownHostsManagerTest.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/KnownHostsManagerTest.java?rev=1355385&view=auto
==============================================================================
--- karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/KnownHostsManagerTest.java (added)
+++ karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/KnownHostsManagerTest.java Fri Jun 29 13:51:16 2012
@@ -0,0 +1,58 @@
+/*
+ * 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.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class KnownHostsManagerTest {
+ private static final String ALGORITHM = "DSA";
+
+ private PublicKey createPubKey() throws NoSuchAlgorithmException {
+ KeyPairGenerator gen = KeyPairGenerator.getInstance(ALGORITHM);
+ KeyPair keyPair = gen.generateKeyPair();
+ return keyPair.getPublic();
+ }
+
+ @Test
+ public void testStoreAndRetrieve() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
+ SocketAddress address = new InetSocketAddress("localhost", 1001);
+ File hostsFile = File.createTempFile("hosts", "");
+ KnownHostsManager manager = new KnownHostsManager(hostsFile);
+
+ PublicKey foundKey1 = manager.getKnownKey(address, ALGORITHM);
+ Assert.assertNull(foundKey1);
+
+ PublicKey serverKey = createPubKey();
+ manager.storeKeyForHost(address, serverKey);
+ PublicKey foundKey2 = manager.getKnownKey(address, ALGORITHM);
+ Assert.assertEquals(serverKey, foundKey2);
+ }
+
+}
Added: karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/ServerKeyVerifierImplTest.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/ServerKeyVerifierImplTest.java?rev=1355385&view=auto
==============================================================================
--- karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/ServerKeyVerifierImplTest.java (added)
+++ karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/ServerKeyVerifierImplTest.java Fri Jun 29 13:51:16 2012
@@ -0,0 +1,88 @@
+/*
+ * 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.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ServerKeyVerifierImplTest {
+
+ private static final InetSocketAddress LOCALHOST = new InetSocketAddress("localhost", 1001);
+ private static final String ALGORITHM = "DSA";
+
+ private PublicKey createPubKey() throws NoSuchAlgorithmException {
+ KeyPairGenerator gen = KeyPairGenerator.getInstance(ALGORITHM);
+ KeyPair keyPair = gen.generateKeyPair();
+ return keyPair.getPublic();
+ }
+
+ @Test
+ public void testNewKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
+ SocketAddress address = LOCALHOST;
+ PublicKey validServerKey = createPubKey();
+
+ KnownHostsManager knowHostsManager = EasyMock.createMock(KnownHostsManager.class);
+ EasyMock.expect(knowHostsManager.getKnownKey(address, ALGORITHM)).andReturn(null);
+ knowHostsManager.storeKeyForHost(address, validServerKey);
+ EasyMock.expectLastCall();
+ EasyMock.replay(knowHostsManager);
+
+ ServerKeyVerifierImpl verifier = new ServerKeyVerifierImpl(knowHostsManager);
+ boolean verified = verifier.verifyServerKey(null, address, validServerKey);
+ Assert.assertTrue("Key should be verified as the key is new", verified);
+ }
+
+ @Test
+ public void testKnownAndCorrectKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
+ SocketAddress address = LOCALHOST;
+ PublicKey validServerKey = createPubKey();
+
+ KnownHostsManager knowHostsManager = EasyMock.createMock(KnownHostsManager.class);
+ EasyMock.expect(knowHostsManager.getKnownKey(address, ALGORITHM)).andReturn(validServerKey);
+ EasyMock.replay(knowHostsManager);
+
+ ServerKeyVerifierImpl verifier = new ServerKeyVerifierImpl(knowHostsManager);
+ boolean verified = verifier.verifyServerKey(null, address, validServerKey);
+ Assert.assertTrue("Key should be verified as the key is known and matches the key we verify", verified);
+ }
+
+ @Test
+ public void testKnownAndIncorrectKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
+ SocketAddress address = LOCALHOST;
+ PublicKey validServerKey = createPubKey();
+ PublicKey otherServerKey = createPubKey();
+
+ KnownHostsManager knowHostsManager = EasyMock.createMock(KnownHostsManager.class);
+ EasyMock.expect(knowHostsManager.getKnownKey(address, ALGORITHM)).andReturn(otherServerKey);
+ EasyMock.replay(knowHostsManager);
+
+ ServerKeyVerifierImpl verifier = new ServerKeyVerifierImpl(knowHostsManager);
+ boolean verified = verifier.verifyServerKey(null, address, validServerKey);
+ Assert.assertFalse("Key should not be verified as the key is known and does not match the key we verify", verified);
+ }
+}