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 2012/04/03 08:01:51 UTC

svn commit: r1308713 - in /karaf/trunk/shell/ssh/src: main/java/org/apache/karaf/shell/ssh/ main/resources/OSGI-INF/blueprint/ test/ test/java/ test/java/org/ test/java/org/apache/ test/java/org/apache/karaf/ test/java/org/apache/karaf/shell/ test/java...

Author: jbonofre
Date: Tue Apr  3 06:01:50 2012
New Revision: 1308713

URL: http://svn.apache.org/viewvc?rev=1308713&view=rev
Log:
[KARAF-32] Add support of SSH key authentication

Added:
    karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/KarafPublickeyAuthenticator.java
    karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/UserAuthFactoriesFactory.java
    karaf/trunk/shell/ssh/src/test/
    karaf/trunk/shell/ssh/src/test/java/
    karaf/trunk/shell/ssh/src/test/java/org/
    karaf/trunk/shell/ssh/src/test/java/org/apache/
    karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/
    karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/
    karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/
    karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/TestAuthorizedKeysParsing.java
    karaf/trunk/shell/ssh/src/test/resources/
    karaf/trunk/shell/ssh/src/test/resources/org/
    karaf/trunk/shell/ssh/src/test/resources/org/apache/
    karaf/trunk/shell/ssh/src/test/resources/org/apache/karaf/
    karaf/trunk/shell/ssh/src/test/resources/org/apache/karaf/shell/
    karaf/trunk/shell/ssh/src/test/resources/org/apache/karaf/shell/ssh/
    karaf/trunk/shell/ssh/src/test/resources/org/apache/karaf/shell/ssh/authorized_keys
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/KarafPublickeyAuthenticator.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/KarafPublickeyAuthenticator.java?rev=1308713&view=auto
==============================================================================
--- karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/KarafPublickeyAuthenticator.java (added)
+++ karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/KarafPublickeyAuthenticator.java Tue Apr  3 06:01:50 2012
@@ -0,0 +1,303 @@
+/*
+ * 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.mina.util.Base64;
+import org.apache.sshd.server.PublickeyAuthenticator;
+import org.apache.sshd.server.session.ServerSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.*;
+
+/**
+ * A public key authenticator, which reads an OpenSSL2 <code>authorized_keys</code> file.
+ */
+public class KarafPublickeyAuthenticator implements PublickeyAuthenticator {
+
+    private static final transient Logger LOGGER = LoggerFactory.getLogger(KarafPublickeyAuthenticator.class);
+
+    private String authorizedKeys;
+    private boolean active;
+
+    public static final class AuthorizedKey {
+
+        private final String alias;
+        private final String format;
+        private final PublicKey publicKey;
+
+        public AuthorizedKey(String alias, String format, PublicKey publicKey) {
+            super();
+            this.alias = alias;
+            this.format = format;
+            this.publicKey = publicKey;
+        }
+
+        public String getAlias() {
+            return this.alias;
+        }
+        
+        public String getFormat() {
+            return this.format;
+        }
+
+        public PublicKey getPublicKey() {
+            return this.publicKey;
+        }
+
+    }
+
+    private static final class PublicKeyComparator implements Comparator<PublicKey> {
+
+        public int compare(PublicKey a, PublicKey b) {
+            if (a instanceof DSAPublicKey) {
+                if (b instanceof DSAPublicKey) {
+                    DSAPublicKey da = (DSAPublicKey) a;
+                    DSAPublicKey db = (DSAPublicKey) b;
+                    int r = da.getParams().getG().compareTo(db.getParams().getG());
+                    if (r != 0) {
+                        return r;
+                    }
+                    r = da.getParams().getP().compareTo(db.getParams().getP());
+                    if (r != 0) {
+                        return r;
+                    }
+                    r = da.getParams().getQ().compareTo(db.getParams().getQ());
+                    if (r != 0) {
+                        return r;
+                    }
+                    return da.getY().compareTo(db.getY());
+                } else {
+                    return -1;
+                }
+            } else if (a instanceof RSAPublicKey) {
+                if (b instanceof RSAPublicKey) {
+                    RSAPublicKey da = (RSAPublicKey) a;
+                    RSAPublicKey db = (RSAPublicKey) b;
+                    int r = da.getPublicExponent().compareTo(db.getPublicExponent());
+                    if (r != 0) {
+                        return r;
+                    }
+                    return da.getModulus().compareTo(db.getModulus());
+                } else {
+                    return -1;
+                }
+            } else {
+                throw new IllegalArgumentException("Only RSA and DAS keys are supported.");
+            }
+        }
+    }
+
+    private final class AuthorizedKeysProvider extends TimerTask {
+
+        private Map<PublicKey, AuthorizedKey> keys;
+        private Long lastModificationDate;
+        private Boolean fileAvailable;
+
+        @Override
+        public void run() {
+            try {
+                File af = new File(KarafPublickeyAuthenticator.this.authorizedKeys);
+                if (af.exists()) {
+                    Long newModificationDate = Long.valueOf(af.lastModified());
+                    if ((this.fileAvailable != null && !this.fileAvailable.booleanValue()) || !newModificationDate.equals(this.lastModificationDate)) {
+                        LOGGER.info("Parsing authorized keys file {}...", KarafPublickeyAuthenticator.this.authorizedKeys);
+                        this.fileAvailable = Boolean.TRUE;
+                        this.lastModificationDate = newModificationDate;
+                        Map<PublicKey, AuthorizedKey> newKeys = KarafPublickeyAuthenticator.parseAuthorizedKeys(new FileInputStream(af));
+                        this.setKeys(newKeys);
+                        LOGGER.info("Successfully parsed {} keys from file {}", newKeys.size(), KarafPublickeyAuthenticator.this.authorizedKeys);
+                    }
+                } else {
+                    if (this.fileAvailable != null && this.fileAvailable.booleanValue()) {
+                        LOGGER.info("Authorized keys file {} disappeared, will recheck every minute", KarafPublickeyAuthenticator.this.authorizedKeys);
+                    } else if (this.fileAvailable == null) {
+                        LOGGER.info("Authorized keys file {} does not exist, will recheck every minute", KarafPublickeyAuthenticator.this.authorizedKeys);
+                    }
+                    this.fileAvailable = Boolean.FALSE;
+                    this.lastModificationDate = null;
+                    this.setKeys(null);
+                }
+            } catch (Throwable e) {
+                LOGGER.error("Error parsing authorized keys file {}", KarafPublickeyAuthenticator.this.authorizedKeys, e);
+                this.fileAvailable = Boolean.FALSE;
+                this.lastModificationDate = null;
+                this.setKeys(null);
+            }
+        }
+
+        private synchronized void setKeys(Map<PublicKey, AuthorizedKey> keys) {
+            this.keys = keys;
+        }
+
+        public synchronized AuthorizedKey getKey(PublicKey publicKey) {
+            if (this.keys == null) {
+                return null;
+            }
+            return this.keys.get(publicKey);
+        }
+
+    }
+
+    private Timer parseAuthorizedKeysTimer;
+    private AuthorizedKeysProvider authorizedKeysProvider;
+
+    private static final int getInt(byte[] b, int pos) {
+        return (((int) b[pos] & 0xff) << 24) +
+                (((int) b[pos+1] & 0xff) << 16) +
+                (((int) b[pos+2] & 0xff) << 8) +
+                ((int) b[pos+3] & 0xff);
+    }
+
+    /**
+     * Parse an <code>authorized_keys</code> file in OpenSSH style.
+     *
+     * @param is the input stream to read.
+     * @return a map of authorized public keys.
+     * @throws IOException
+     * @throws NoSuchAlgorithmException
+     * @throws InvalidKeySpecException
+     */
+    public static final Map<PublicKey, AuthorizedKey> parseAuthorizedKeys(InputStream is) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
+        try {
+            Base64 decoder = new Base64();
+
+            KeyFactory rsaKeyGen = KeyFactory.getInstance("RSA");
+            KeyFactory dsaKeyGen = KeyFactory.getInstance("DSA");
+
+            LineNumberReader reader = new LineNumberReader(new InputStreamReader(is, "UTF-8"));
+            
+            Map<PublicKey, AuthorizedKey> ret = new TreeMap<PublicKey, AuthorizedKey>(new PublicKeyComparator());
+
+            String line;
+
+            while ((line = reader.readLine()) != null) {
+                String[] tokens = line.split("[ \\t]+", 3);
+                if (tokens.length != 3) {
+                    throw new IOException("Authorized keys file line " + reader.getLineNumber() + " does not contain 3 tokens.");
+                }
+                byte[] rawKey = decoder.decode(tokens[1].getBytes("UTF-8"));
+                if (getInt(rawKey, 0) != 7 || !new String(rawKey, 4, 7, "UTF-8").equals(tokens[0])) {
+                    throw new IOException("Authorized keys file line " + reader.getLineNumber() + " contains a key with a format that does not match the first token.");
+                }
+                PublicKey pk;
+                if (tokens[0].equals("ssh-dss")) {
+                    int pos = 11;
+
+                    int n = getInt(rawKey, pos);
+                    pos += 4;
+                    BigInteger p = new BigInteger(1, Arrays.copyOfRange(rawKey, pos, pos + n));
+                    pos += n;
+
+                    n = getInt(rawKey, pos);
+                    pos += 4;
+                    BigInteger q = new BigInteger(1, Arrays.copyOfRange(rawKey, pos, pos + n));
+                    pos += n;
+
+                    n = getInt(rawKey, pos);
+                    pos += 4;
+                    BigInteger g = new BigInteger(1, Arrays.copyOfRange(rawKey, pos, pos + n));
+                    pos += n;
+
+                    n = getInt(rawKey, pos);
+                    pos += 4;
+                    BigInteger y = new BigInteger(1, Arrays.copyOfRange(rawKey, pos, pos + n));
+                    pos += n;
+
+                    if (pos != rawKey.length) {
+                        throw new IOException("Authorized keys file line " + reader.getLineNumber() + " contains a DSA key with extra garbage.");
+                    }
+
+                    DSAPublicKeySpec ps = new DSAPublicKeySpec(y, p, q, g);
+                    pk = dsaKeyGen.generatePublic(ps);
+                } else if (tokens[0].equals("ssh-rsa")) {
+                    int pos = 11;
+
+                    int n = getInt(rawKey, pos);
+                    pos += 4;
+                    BigInteger e = new BigInteger(1, Arrays.copyOfRange(rawKey, pos, pos + n));
+                    pos += n;
+
+                    n = getInt(rawKey, pos);
+                    pos += 4;
+                    BigInteger modulus = new BigInteger(1, Arrays.copyOfRange(rawKey, pos, pos + n));
+                    pos += n;
+
+                    if (pos != rawKey.length) {
+                        throw new IOException("Authorized keys file line " + reader.getLineNumber() + " contains a RSA key with extra garbage.");
+                    }
+
+                    RSAPublicKeySpec ps = new RSAPublicKeySpec(modulus, e);
+                    pk = rsaKeyGen.generatePublic(ps);
+                } else {
+                    throw new IOException("Authorized keys file line " + reader.getLineNumber() + " does not start with ssh-dss or ssh-rsa.");
+                }
+                
+                ret.put(pk, new AuthorizedKey(tokens[2], tokens[0], pk));
+            }
+            return ret;
+        } finally {
+            is.close();
+        }
+    }
+
+    public boolean authenticate(String username, PublicKey publicKey, ServerSession session) {
+        AuthorizedKey ak = this.authorizedKeysProvider.getKey(publicKey);
+        if (ak == null) {
+            LOGGER.error("Failed authenticate of user {} from {} with unknown public key.", username, session.getIoSession().getRemoteAddress());
+            return false;
+        }
+        LOGGER.info("Successful authentication of user {} from {} with public key {}.", new Object[]{ username, session.getIoSession().getRemoteAddress(), ak.getAlias() });
+        return true;
+    }
+
+    public void setAuthorizedKeys(String path) {
+        this.authorizedKeys = path;
+    }
+
+    public void setActive(boolean active) {
+        this.active = active;
+    }
+
+    public void startTimer() {
+        if (this.active) {
+            this.parseAuthorizedKeysTimer = new Timer();
+            this.authorizedKeysProvider = new AuthorizedKeysProvider();
+            this.parseAuthorizedKeysTimer.schedule(this.authorizedKeysProvider, 10, 60000L);
+        }
+    }
+
+    public void stopTimer() {
+        if (this.parseAuthorizedKeysTimer != null) {
+            this.parseAuthorizedKeysTimer.cancel();
+            this.parseAuthorizedKeysTimer = null;
+        }
+    }
+
+}

Added: karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/UserAuthFactoriesFactory.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/UserAuthFactoriesFactory.java?rev=1308713&view=auto
==============================================================================
--- karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/UserAuthFactoriesFactory.java (added)
+++ karaf/trunk/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/UserAuthFactoriesFactory.java Tue Apr  3 06:01:50 2012
@@ -0,0 +1,112 @@
+/*
+ * 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.sshd.common.NamedFactory;
+import org.apache.sshd.server.UserAuth;
+import org.apache.sshd.server.auth.UserAuthPassword;
+import org.apache.sshd.server.auth.UserAuthPublicKey;
+import org.osgi.service.blueprint.container.ComponentDefinitionException;
+import org.osgi.service.blueprint.container.Converter;
+import org.osgi.service.blueprint.container.ReifiedType;
+
+import java.lang.reflect.ParameterizedType;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * <p>A factory for user authentication factories to set on
+ * {@link SshServer#setUserAuthFactories(java.util.List)} based on a
+ * comma-separated list of authentication methods.</p>
+ *
+ * <p>Currently, the following methods are supported:</p>
+ * <ul>
+ *     <li><code>password</code>
+ *          Password authentication against a given JAAS domain.</p></li>
+ *     <li><code>publickey</code>
+ *          Public key authentication against an OpenSSH <code>authorized_keys</code> file.</p></li>
+ * </ul>
+ * </p>
+ */
+public class UserAuthFactoriesFactory {
+
+    public static final String PASSWORD_METHOD = "password";
+    public static final String PUBLICKEY_METHOD = "publickey";
+
+    private Set<String> methodSet;
+    private List<NamedFactory<UserAuth>> factories;
+
+    public static Converter getConverter() {
+        return new Converter();
+    }
+
+    /**
+     * This blueprint type converter silently converts instances of
+     * <code>Class X implements NameFactory</code>
+     * to the reified type <code>cNameFactory</code>
+     * and therefore helps blueprint to set the returned factories on
+     * {@link SshServerAction#setUserAuthFactories(List)} without complaining
+     * about type conversion errors.
+     */
+    public static class Converter implements org.osgi.service.blueprint.container.Converter {
+
+        public boolean canConvert(Object sourceObject, ReifiedType targetType) {
+            return NamedFactory.class.isAssignableFrom(sourceObject.getClass())
+                    && UserAuth.class.equals(((ParameterizedType) sourceObject.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0])
+                    && NamedFactory.class.equals(targetType.getRawClass())
+                    && UserAuth.class.equals(targetType.getActualTypeArgument(0).getRawClass());
+        }
+
+        public Object convert(Object sourceObject, ReifiedType targetType) throws Exception {
+            return sourceObject;
+        }
+
+    }
+
+    public void setAuthMethods(String methods) {
+        this.methodSet = new HashSet<String>();
+        this.factories = new ArrayList<NamedFactory<UserAuth>>();
+        String[] ams = methods.split(",");
+        for (String am : ams) {
+            if (PASSWORD_METHOD.equals(am)) {
+                this.factories.add(new UserAuthPassword.Factory());
+            } else if (PUBLICKEY_METHOD.equals(am)) {
+                this.factories.add(new UserAuthPublicKey.Factory());
+            } else {
+                throw new ComponentDefinitionException("Invalid authentication method " + am + " specified");
+            }
+            this.methodSet.add(am);
+        }
+    }
+
+    public List<NamedFactory<UserAuth>> getFactories() {
+        return factories;
+    }
+
+    public boolean isPublickeyEnabled() {
+        return this.methodSet.contains(PUBLICKEY_METHOD);
+    }
+
+    public boolean isPasswordEnabled() {
+        return this.methodSet.contains(PASSWORD_METHOD);
+    }
+
+}

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=1308713&r1=1308712&r2=1308713&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 Tue Apr  3 06:01:50 2012
@@ -25,6 +25,7 @@
 
     <type-converters>
         <bean class="org.apache.karaf.shell.ssh.ShellFactoryImpl" factory-method="getConverter" />
+        <bean class="org.apache.karaf.shell.ssh.UserAuthFactoriesFactory" factory-method="getConverter" />
     </type-converters>
 
     <ext:property-placeholder placeholder-prefix="$[" placeholder-suffix="]">
@@ -41,6 +42,8 @@
             <cm:property name="sshRealm" value="karaf"/>
             <cm:property name="sshRole" value="$[karaf.admin.role]"/>
             <cm:property name="hostKey" value="${karaf.base}/etc/host.key"/>
+            <cm:property name="authorizedKeys" value="${karaf.base}/etc/authorized_keys"/>
+            <cm:property name="authMethods" value="password,publickey"/>
         </cm:default-properties>
     </cm:property-placeholder>
 
@@ -64,6 +67,10 @@
     <bean id="sshClient" class="org.apache.sshd.SshClient" factory-method="setUpDefaultClient" scope="prototype">
     </bean>
 
+    <bean id="userAuthFactoriesFactory" class="org.apache.karaf.shell.ssh.UserAuthFactoriesFactory">
+        <property name="authMethods" value="${authMethods}"/>
+    </bean>
+
     <bean id="sshServer" class="org.apache.sshd.SshServer" factory-method="setUpDefaultServer" scope="prototype">
         <property name="port" value="${sshPort}"/>
         <property name="host" value="${sshHost}"/>
@@ -83,9 +90,13 @@
         </property>
         <property name="keyPairProvider" ref="keyPairProvider"/>
         <property name="passwordAuthenticator" ref="passwordAuthenticator"/>
+        <property name="publickeyAuthenticator" ref="publickeyAuthenticator"/>
         <property name="fileSystemFactory">
             <bean class="org.apache.karaf.shell.ssh.KarafFileSystemFactory"/>
         </property>
+        <property name="userAuthFactories">
+            <bean factory-ref="userAuthFactoriesFactory" factory-method="getFactories"/>
+        </property>
     </bean>
 
     <bean id="keyPairProvider" class="org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider">
@@ -96,6 +107,14 @@
         <property name="role" value="${sshRole}"/>
     </bean>
 
+    <bean id="publickeyAuthenticator" class="org.apache.karaf.shell.ssh.KarafPublickeyAuthenticator"
+            init-method="startTimer" destroy-method="stopTimer">
+        <property name="authorizedKeys" value="${authorizedKeys}"/>
+        <property name="active">
+            <bean factory-ref="userAuthFactoriesFactory" factory-method="isPublickeyEnabled"/>
+        </property>
+    </bean>
+
     <bean id="sshServerFactory" class="org.apache.karaf.shell.ssh.SshServerFactory" init-method="start"
           destroy-method="stop" activation="eager">
         <argument ref="sshServer"/>

Added: karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/TestAuthorizedKeysParsing.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/TestAuthorizedKeysParsing.java?rev=1308713&view=auto
==============================================================================
--- karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/TestAuthorizedKeysParsing.java (added)
+++ karaf/trunk/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/TestAuthorizedKeysParsing.java Tue Apr  3 06:01:50 2012
@@ -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;
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Map;
+
+/**
+ * Test parsing an authorized_keys file.
+ */
+public class TestAuthorizedKeysParsing extends TestCase {
+
+    public void testAuthorizedKeysParsing() throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
+        InputStream is = TestAuthorizedKeysParsing.class.getClassLoader().getResourceAsStream("org/apache/karaf/shell/ssh/authorized_keys");
+        Map<PublicKey, KarafPublickeyAuthenticator.AuthorizedKey> keys = KarafPublickeyAuthenticator.parseAuthorizedKeys(is);
+        assertEquals(2, keys.size());
+        for (Map.Entry<PublicKey, KarafPublickeyAuthenticator.AuthorizedKey> e : keys.entrySet()) {
+            assertSame(e.getKey(), e.getValue().getPublicKey());
+            assertTrue("ssh-dss".equals(e.getValue().getFormat()) || "ssh-rsa".equals(e.getValue().getFormat()));
+
+            if ("ssh-dss".equals(e.getValue().getFormat())) {
+                assertTrue(e.getKey() instanceof DSAPublicKey);
+                assertEquals("dsa-test", e.getValue().getAlias());
+            }
+            if ("ssh-rsa".equals(e.getValue().getFormat())) {
+                assertTrue(e.getKey() instanceof RSAPublicKey);
+                assertEquals("rsa-test", e.getValue().getAlias());
+            }
+        }
+    }
+
+}

Added: karaf/trunk/shell/ssh/src/test/resources/org/apache/karaf/shell/ssh/authorized_keys
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/ssh/src/test/resources/org/apache/karaf/shell/ssh/authorized_keys?rev=1308713&view=auto
==============================================================================
--- karaf/trunk/shell/ssh/src/test/resources/org/apache/karaf/shell/ssh/authorized_keys (added)
+++ karaf/trunk/shell/ssh/src/test/resources/org/apache/karaf/shell/ssh/authorized_keys Tue Apr  3 06:01:50 2012
@@ -0,0 +1,2 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dsa-test
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1on8gxCGJJWSRT4uOrR13mUaUk0hRf4RzxSZ1zRbYYFw8pfGesIFoEuVth4HKyF8k1y4mRUnYHP1XNMNMJl1JcEArC2asV8sHf6zSPVffozZ5TT4SfsUu/iKy9lUcCfXzwre4WWZSXXcPff+EHtWshahu3WzBdnGxm5Xoi89zcE= rsa-test
\ No newline at end of file