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


Reply via email to