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